Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to start Play application before tests and then shut it down in specs2?

My goal is to get an application running and execute multiple tests on the same instance of the app.

I have tried this solution without much luck. Here is my test:

class ApplicationSpec extends Specification { sequential


  object AppWithTestDb2 extends FakeApplication(additionalConfiguration = 
    Map(
    ("db.default.driver") -> "org.h2.Driver",
    ("db.default.url") -> (
//        "jdbc:h2:mem:play-test-" + scala.util.Random.nextInt +      // in memory database 
        "jdbc:h2:/tmp/play-test-" + scala.util.Random.nextInt +     // file based db that can be accessed using h2-browse in activator
        ";MODE=PostgreSQL;DATABASE_TO_UPPER=false;DB_CLOSE_DELAY=-1")
    ))

    running(AppWithTestDb2) {

      "Application" should {

        "send 404 on a bad request" in {
          route(FakeRequest(GET, "/boum")) must beNone
        }

        "post signup request" in {
          val res = route(FakeRequest(POST, "/user", FakeHeaders(), TestData.newUser)).get
                  status(res) must equalTo(OK)
                  contentType(res) must beSome("application/json")
                  contentAsJson(res) mustEqual TestData.newUser
        }

        "fail signup request for existing user" in {
          val res1 = route(FakeRequest(POST, "/user", FakeHeaders(), TestData.newUser)).get
                  Await.result(res1, Duration.Inf)
                  val res = route(FakeRequest(POST, "/user", FakeHeaders(), TestData.newUser)).get
                  Await.result(res, Duration.Inf)
                  status(res) must equalTo(CONFLICT)
                  contentType(res) must beSome("application/json")
                  contentAsJson(res) mustEqual TestData.newUser
        }

    }
  }

}

The problem here is that application starts and stops immediately and tests are executed without a running application:

[debug] c.j.b.BoneCPDataSource - JDBC URL = jdbc:h2:/tmp/play-test--437407884;MODE=PostgreSQL;DATABASE_TO_UPPER=false;DB_CLOSE_DELAY=-1, Username = zalando, partitions = 1, max (per partition) = 30, min (per partition) = 5, idle max age = 10 min, idle test period = 1 min, strategy = DEFAULT
[info] application - Application has started
[debug] application - Binding to Slick DAO implementations.
[info] application - Application shutdown...
[debug] c.j.b.BoneCPDataSource - Connection pool has been shut down
[info] ApplicationSpec
[info] 
[info] Application should
[info] ! send 404 on a bad request
[error]    RuntimeException: : There is no started application  (Play.scala:71)
[error] play.api.Play$$anonfun$current$1.apply(Play.scala:71)
[error] play.api.Play$$anonfun$current$1.apply(Play.scala:71)
[error] play.api.Play$.current(Play.scala:71)
[error] play.api.test.RouteInvokers$class.route(Helpers.scala:305)
[error] play.api.test.Helpers$.route(Helpers.scala:403)
[error] ApplicationSpec$$anonfun$1$$anonfun$apply$7$$anonfun$apply$8$$anonfun$apply$9.apply(ApplicationSpec.scala:76)
[error] ApplicationSpec$$anonfun$1$$anonfun$apply$7$$anonfun$apply$8$$anonfun$apply$9.apply(ApplicationSpec.scala:76)
[error] ApplicationSpec$$anonfun$1$$anonfun$apply$7$$anonfun$apply$8.apply(ApplicationSpec.scala:76)
[error] ApplicationSpec$$anonfun$1$$anonfun$apply$7$$anonfun$apply$8.apply(ApplicationSpec.scala:76)
like image 759
Gediminas Aleknavičius Avatar asked Feb 12 '23 07:02

Gediminas Aleknavičius


2 Answers

Here is my working solution

object AppWithTestDb2 extends FakeApplication(additionalConfiguration = 
Map(
    ("db.default.driver") -> "org.h2.Driver",
    ("db.default.url") -> (
//        "jdbc:h2:mem:play-test-" + scala.util.Random.nextInt +      // in memory database 
        "jdbc:h2:/tmp/play-test-" + scala.util.Random.nextInt +     // file based db that can be accessed using h2-browse in activator
        ";MODE=PostgreSQL;DATABASE_TO_UPPER=false;DB_CLOSE_DELAY=-1")
))


class SignupLoginSpec extends Specification { sequential


    step(Play.start(AppWithTestDb2))

  "application" should {

    "post signup request" in {
      val res = route(FakeRequest(POST, "/user", FakeHeaders(), TestData.newUser)).get
      status(res) must equalTo(OK)
      contentType(res) must beSome("application/json")
      contentAsJson(res) mustEqual TestData.newUser
    }

    "fail signup request for existing user" in {
      val res1 = route(FakeRequest(POST, "/user", FakeHeaders(), TestData.newUser)).get
      Await.result(res1, Duration.Inf)
      val res = route(FakeRequest(POST, "/user", FakeHeaders(), TestData.newUser)).get
      Await.result(res, Duration.Inf)
      status(res) must equalTo(CONFLICT)
      contentType(res) must beSome("application/json")
      contentAsJson(res) mustEqual TestData.newUser
    }

  }

  step(Play.stop())

}
like image 184
Gediminas Aleknavičius Avatar answered Feb 16 '23 03:02

Gediminas Aleknavičius


In specs2 there is a distinction between test declaration and test execution. When you write "application" should ... you just declare tests. The executable part is what is enclosed in the ... in ... part.

So when you declare running(AppWithTestDb2) { ... } you just create some tests inside the context of an AppTestDb2 application.

The general solution for what you want to achieve in specs2 is to use Steps like this:

class ApplicationSpec extends Specification { sequential

  step("start application")

  "Application" should {
    "send 404 on a bad request" in { ... }

    "post signup request" in { ... }
  }

  step("stop application")
}

Then, the way the specs2 execution model works, you will get your fake application started before all the tests start and terminated when all the tests are finished (whether you use sequential or not)

I am not a Play user but I suspect that you should be able to reuse the WithApplication class or something similar to create your start/stop steps. Otherwise there is a blog post here exploring a solution for the same problem.

like image 23
Eric Avatar answered Feb 16 '23 03:02

Eric