8

Running tests as described here

"Spec" should {
  "example" in new WithApplication {
    ...
  }
}

is unacceptably slow for me. This is because new WithApplication is starting and stoping framework at every example. Don't get me wrong, a framework itself loads very fast, but if database is configured (surprise!), situation becomes terrible.

Here are some measurments:

"The database layer" should {

  "test1" in  {
    1 must be equalTo(1)
  }
  ...
  "test20" in {
    1 must be equalTo(1)
  }
}

Execution time: 2 seconds. Same test with WithApplication at every example consumes 9 seconds

I was able to achive much better results thanks to this answer

import play.api.Play
import play.api.test.FakeApplication
import org.specs2.mutable.Specification
import scalikejdbc._

class MySpec extends Specification {

var fake: FakeApplication = _

step {fake = FakeApplication(...)}
step {Play.start(fake)}

"The database layer" should {

  "some db test" in {
    DB localTx { implicit session =>
      ...
    }
  }

  "another db test" in {
    DB localTx { implicit session =>
      ...
    }
  }

  step {Play.stop()}
}

}

Pros: performance boost

Cons:

  • need to copy-paste setup and tear-down code because don't know how to reuse it (by reuse I mean something like "class MySpec extends Specification with NoWasteOfTime"

  • new WithApplication() calls Helpers.running which looks like this

synchronized {
  try {
    Play.start(fakeApp)
    block
  } finally {
    Play.stop()
    play.api.libs.ws.WS.resetClient()
  }
}

so I can't completely emulate Helpers.running behaviour (resetClient is not visible for my code) without reflection.

Please suggest how to break cons or different approach how accomplish my issue.

Community
  • 1
  • 1
Jeriho
  • 7,129
  • 9
  • 41
  • 57
  • Do you want to setup/teardown once for a specification, or do you want to do it for every test within it? – Michael Zajac Nov 28 '13 at 15:37
  • It's too bad that it's 18 months later and Play still hasn't changed this behaviour. Starting the app for each test is ludicrous. Play 3 will have DI that will hopefully remove the need for a running app altogether. – andyczerwonka Apr 14 '15 at 18:20

1 Answers1

9

I don't know if it is the best possible solution but in this thread: Execute code before and after specification

You can read a solution for reusable code. I implemented it with little modifications. For me the beforeAll step did not run and added the sequential modifier.

import org.specs2.mutable._
import org.specs2.specification._

class PlayAppSpec extends Specification with BeforeAllAfterAll{
    sequential
    lazy val app : FakeApplication = {
        FakeApplication()
    }

    def beforeAll(){
        Play.start(app)
    }

    def afterAll(){
        Play.stop()
    }
}
import org.specs2.specification.Step

trait BeforeAllAfterAll extends Specification {
  // see specs2 User Guide, linked below
  override def map(fragments: =>Fragments) = {
    beforeAll()
    fragments ^ Step(afterAll)
  }
    

  def beforeAll()
  def afterAll()
}

I think the map would be better with Step(...) ^ fragments ^ Step(...) but it did not run the beforeAll for me. The user guide at "Global setup/teardown" says to use a lazy val.

Overall it was a pain to set up this. My problem was Exception in thread "Thread-145" java.net.SocketException: Connection reset Or

Configuration error[Cannot connect to database [default]] (Configuration.scala:559)

When reusing the same FakeApplication: SQLException: Attempting to obtain a connection from a pool that has already been shutdown.

I think it is much more logical this way than always creating a new application for every "in" block or adding all tests into one block.

General Grievance
  • 4,555
  • 31
  • 31
  • 45
Lajos Gerecs
  • 106
  • 2