Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fast test execution in a playframework fake application

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.

like image 388
Jeriho Avatar asked Nov 27 '13 22:11

Jeriho


1 Answers

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 http://bit.ly/11I9kFM (specs2 User Guide)
  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 (http://bit.ly/11I9kFM) 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.

like image 50
Lajos Gerecs Avatar answered Sep 18 '22 18:09

Lajos Gerecs