Reputation: 174
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
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