Scala / Specs2 : def is(implicit ee: ExecutionEnv) = { "Service" should {...} }

139 Views Asked by At

I try to upgrade a Scala/Play project to Play 2.7, Scala 2.12.11, Specs2 4.5.1.

In the project there is the following Specs2 test that I cannot understand in the sense of its Specs2 specification structure (could be that the Specs2 API changed a lot since the test was written).

When I looked at the structure of specifications in the current API, I could not see any example of is method combined together with should.

What was it supposed to mean?

How can I rewrite such a specification in the current Specs2 API?

I also noticed that the test code used import org.specs2.mutable.Specification instead of import org.specs2.Specification which is supposed to be used when using the is method. And it uses def is(implicit ee: ExecutionEnv), instead of def is.

Here is the old test:

package services

import org.specs2.concurrent.ExecutionEnv
import org.specs2.mutable.Specification
import org.specs2.specification.mutable.ExecutionEnvironment
import play.api.inject.guice.GuiceApplicationBuilder
import play.api.test.WithApplication
import play.modules.reactivemongo._

import scala.concurrent.duration._

class StatisticsServiceSpec() extends Specification with ExecutionEnvironment {

  def is(implicit ee: ExecutionEnv) = {
    "The StatisticsService" should {

      "compute and publish statistics" in new WithApplication() {
        val repository = new MongoStatisticsRepository(configuredAppBuilder.injector.instanceOf[ReactiveMongoApi])
        val wsTwitterService = new WSTwitterService
        val service = new DefaultStatisticsService(repository, wsTwitterService)

        val f = service.createUserStatistics("elmanu")

        f must beEqualTo(()).await(retries = 0, timeout = 5.seconds)
      }

    }

    def configuredAppBuilder = {
      import scala.collection.JavaConversions.iterableAsScalaIterable

      val env = play.api.Environment.simple(mode = play.api.Mode.Test)
      val config = play.api.Configuration.load(env)
      val modules = config.getStringList("play.modules.enabled").fold(
        List.empty[String])(l => iterableAsScalaIterable(l).toList)

      new GuiceApplicationBuilder().
        configure("play.modules.enabled" -> (modules :+
          "play.modules.reactivemongo.ReactiveMongoModule")).build
    }
  }

}

To simplify the code down to the actual Specs2 API, I think it could be reduced to something like this:

package services

import org.specs2.concurrent.ExecutionEnv
import org.specs2.mutable.Specification

import scala.concurrent.Future
import play.api.test.WithApplication

import scala.concurrent.duration._

class StatisticsServiceSpec(implicit ee: ExecutionEnv) extends Specification /* with ExecutionEnvironment */ {

  def is(implicit ee: ExecutionEnv) = {

    "The StatisticsService" should {

      "compute and publish statistics" in new WithApplication() {

        val f = Future(1)

        f must beEqualTo(1).await(retries = 0, timeout = 5.seconds)
      }
    }
  }
}

Pay attention that I removed the ExecutionEnvironment trait, since it seems to have been removed from the library.

Now, the code finally compiles, but when I try to run the test, there are no errors, but no test is actually run: the output is Empty test suite.

1

There are 1 best solutions below

2
Eric On

The new specification should be

package services

import org.specs2.concurrent.ExecutionEnv
import org.specs2.mutable.Specification

import scala.concurrent.Future
import play.api.test.WithApplication

import scala.concurrent.duration._

class StatisticsServiceSpec(implicit ee: ExecutionEnv) extends Specification {
  "The StatisticsService" should {
    "compute and publish statistics" in new WithApplication() {
      val f = Future(1)
      f must beEqualTo(1).await(retries = 0, timeout = 5.seconds)
    }
  }
}

The ExecutionEnv is now really supposed to be retrieved as a specification member directly (with an implicit to be make it available to the await method).

is is not necessary in a "mutable" specification. is is the function in a Specification where you declare all the "Fragments" of your specification (a Fragment is a Description + an Execution). In a mutable specification this function is automatically populated from the fact that you trigger method calls directly in the body of the class when the specification is instantiated. The fragments created by should and in are collected in a mutable variable, hence the name "mutable specification".

If you define def is(implicit ee: ExecutionEnv), this is like defining another, valid, is definition that specs2 doesn't know about, while not creating anything for the def is: Fragments method. That's why you end up with an empty specification.