skilgal
skilgal

Reputation: 174

Find specifications by pattern in scala specs2

We have an issue with different environments for our integration tests and start/stop latency.

So, we want to run all environment dependencies before all integration tests and shut down after all. We can't just use docker since we use GuiceApplication (starts from the code), EmbeddedKafka (starts from the code), and WireMock(starts from the code). :)

We use scala as a main language and Specs2 as testing library. I've found that we can create one spec, add before/after all code to set up env, and override the is method, which represents the test. We can join a few other Specs into one by

override def is = Seq(new First, new Second)
  .map(_.is)
  .foldLeft(Fragments.empty) { case (l, r) => l.append(r.fragments)}

But I want to automatically gather specs into is. I see a SpecificationFinder trait with methods such as findSpecifications and specifications, but I can't make them return a non-empty list. We have a root directory called 'integrations,' where we store all needed Specs.

Maybe someone has already implemented the search over tests. It looks like these methods are used during the sbt test task, but I didn't find a way to reuse it as I want

Upvotes: 0

Views: 94

Answers (1)

Eric
Eric

Reputation: 15557

There are two answers to that questions depending on the specs2 version that you are using.

specs2-4.x (full example here)

With this version you can collect specifications using a SpecificationsFinder and aggregate them as one big specification with one step before and one step after to start and stop shared services:

import org.specs2._
import org.specs2.specification.core._
import org.specs2.control._
import org.specs2.io._
import runner._
import user.integration.database._

class IntegrationSpec extends Specification { def is = sequential ^ s2"""
  ${step(db.start)}

  Integration specifications
  $integration

  ${step(db.shutdown)}
"""

  def integration =
    Fragments.foreach(specifications) { specification =>
s2"""
  ${specification.is.header.name} ${specification.is.fragments}
  $p
"""
  }

  val specifications: List[SpecificationStructure] = SpecificationsFinder.findSpecifications(
    // change this pattern if the specifications must be found in specific directories, or with specific names
    glob = "**/*.scala",
    // this pattern detects the name of a specification class inside a scala file
    pattern = "(.*Spec)\\s*extends\\s*.*",
    // this additional filter can be used to filter specifications names
    // in this case we avoid infinite recursive execution
    filter = { (name: String) => !name.endsWith("IntegrationSpec") },
    // this is the base directory where specifications must be searched
    basePath = DirectoryPath.unsafe(new java.io.File("src/test/scala").getAbsolutePath),
    verbose = true).runOption.getOrElse(Nil)

}

specs2-5.x (full example here)

With more recent version of specs2 a notion of "global resource" has been introduced so that each individual specification can extend a Resource[R] trait (where R is a global resource, here is an example with a Database):

trait StartDatabase(using ec: ExecutionContext) extends Resource[Database]:
  override def resourceKey: Option[String] =
    Some("shared database")

  def acquire: Future[Database] =
    Future.successful(Database().start)

  def release(db: Database): Execution =
    Future { db.shutdown; true }

and in that case the steps used in the previous IntegrationSpec are not necessary anymore.

Upvotes: 1

Related Questions