Search code examples
scalaunit-testingtestingplayframework-2.0specs2

Alternative of OneAppPerSuite in specs2 scala testing


I am writing unit test cases using specs2 and my application is started and stopped for each test instances.

import org.specs2.mutable._

class HelloWorldSpec extends Specification {

  "The 'Hello world' string" should {
    "contain 11 characters" in new WithApplication {
      "Hello world" must have size(11)
    }
    "start with 'Hello'" in new WithApplication {
      "Hello world" must startWith("Hello")
    }
    "end with 'world'" in new WithApplication {
      "Hello world" must endWith("world")
    }
  }
}

As mentioned in the documentation for each test case application is started and stopped.

I have found a workaround from the link. Application initializes only once (I haven' tested it yet) for each Test Class.

import org.specs2.mutable._

class HelloWorldSpec extends Specification {sequential

  step(Play.start(App)) //supposedly App is iniatilized

  "The 'Hello world' string" should {
    "contain 11 characters" in {
      "Hello world" must have size(11)
    }
    "start with 'Hello'" in {
      "Hello world" must startWith("Hello")
    }
    "end with 'world'" in {
      "Hello world" must endWith("world")
    }
  }
  step(Play.stop())
}

But what if we have multiple classes and we want a single start and stop of the app.

import org.specs2.mutable._

class HelloWorldSpec extends Specification {sequential

  step(Play.start(App)) //supposedly App is iniatilized

  "The 'Hello world' string" should {
    "contain 11 characters" in {
      "Hello world" must have size(11)
    }
    "start with 'Hello'" in {
      "Hello world" must startWith("Hello")
    }
    "end with 'world'" in {
      "Hello world" must endWith("world")
    }
  }
  step(Play.stop())
}

import org.specs2.mutable._

class HitchHikerSpec extends Specification {sequential

  step(Play.start(App)) //supposedly App is iniatilized

  "The 'Hitch Hiker' string" should {
    "contain 11 characters" in {
      "Hitch Hiker" must have size(11)
    }
    "start with 'Hitch'" in {
      "Hitch Hiker" must startWith("Hitch")
    }
    "end with 'Hiker'" in {
      "Hitch Hiker" must endWith("Hiker")
    }
  }
  step(Play.stop())
}

How would I start and stop app for once?

There is a similar solution implemented in scalatest using OneAppPerSuite. Here is the link and example.

import play.api.test._
import org.scalatest._
import org.scalatestplus.play._
import play.api.{Play, Application}
import play.api.inject.guice._

// This is the "master" suite
class NestedExampleSpec extends Suites(
  new OneSpec,
  new TwoSpec,
  new RedSpec,
  new BlueSpec
) with OneAppPerSuite {
  // Override app if you need an Application with other than non-default parameters.
  implicit override lazy val app: Application =
    new GuiceApplicationBuilder().configure(Map("ehcacheplugin" -> "disabled")).build()
}

// These are the nested suites
@DoNotDiscover class OneSpec extends PlaySpec with ConfiguredApp
@DoNotDiscover class TwoSpec extends PlaySpec with ConfiguredApp
@DoNotDiscover class RedSpec extends PlaySpec with ConfiguredApp

@DoNotDiscover
class BlueSpec extends PlaySpec with ConfiguredApp {

  "The OneAppPerSuite trait" must {
    "provide an Application" in {
      app.configuration.getString("ehcacheplugin") mustBe Some("disabled")
    }
    "make the Application available implicitly" in {
      def getConfig(key: String)(implicit app: Application) = app.configuration.getString(key)
      getConfig("ehcacheplugin") mustBe Some("disabled")
    }
    "start the Application" in {
      Play.maybeApplication mustBe Some(app)
    }
  }
}

Can something similar be implemented in specs2?


Solution

  • With specs2 you can do something similar with specification references:

    class SuiteSpec extends Specification { def is = s2"""
      ${link(StartSpec).hide}
      ${ "first spec"  ~ new Spec1Spec }
      ${ "second spec" ~ new Spec2Spec }
      ${link(StopSpec).hide}
      """
    }
    
    object StartSpec extends Specification { def is = s2"""
      ${step(println("start"))}
      """
    }
    
    class Spec1Spec extends Specification { def is = s2"""
      example1 $e1
      """
    
      def e1 = { println("example1"); ok }
    }
    
    class Spec2Spec extends Specification { def is = s2"""
      example2 $e2
      """
    
      def e2 = { println("example2"); ok }
    }
    
    object StopSpec extends Specification { def is = s2"""
      ${step(println("stop"))}
      """
    }
    

    Then if you run:

    testOnly *Suite* -- all
    

    You should see the following lines printed out:

    start
    example1
    example2
    stop