Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ lazy val root = (project in file("."))
.aggregate(
core,
munit,
specs2,
scalatest,
scalatestSelenium,
jdbc,
Expand Down Expand Up @@ -252,6 +253,15 @@ lazy val munit = (project in file("test-framework/munit"))
libraryDependencies ++= Dependencies.munit.value
)

lazy val specs2 = (project in file("test-framework/specs2"))
.dependsOn(core % "compile->compile;test->test;provided->provided")
.settings(commonSettings)
.settings(
name := "testcontainers-scala-specs2",
libraryDependencies ++= Dependencies.specs2.value,
Test / scalacOptions --= Seq("-Xfatal-warnings")
)

lazy val scalatestSelenium = (project in file("test-framework/scalatest-selenium"))
.dependsOn(scalatest % "compile->compile;test->test;provided->provided")
.settings(commonSettings)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,19 @@ package com.dimafeng.testcontainers

import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpecLike
import org.testcontainers.utility.DockerImageName

import scala.collection.JavaConverters._

class FirestoreEmulatorContainerSpec extends AnyWordSpecLike with Matchers with ForAllTestContainer {
class FirestoreEmulatorContainerSpec
extends AnyWordSpecLike
with Matchers
with ForAllTestContainer {

override val container: FirestoreEmulatorContainer = FirestoreEmulatorContainer()
override val container: FirestoreEmulatorContainer =
FirestoreEmulatorContainer(
DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:382.0.0")
)

"Firestore emulator container" should {

Expand Down
15 changes: 15 additions & 0 deletions project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ object Dependencies {
private val scalaTestSeleniumVersion_scala3 = "3.2.9.0"
private val junitVersion = "4.13.2"
private val munitVersion = "1.1.1"

private val specs2Version = Def.setting {
CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, _)) => "4.20.5"
case _ => "5.6.4" // Scala 3
}
}
private val mysqlConnectorVersion = "5.1.42"
private val neo4jConnectorVersion = "4.0.0"
private val oracleDriverVersion = "21.18.0.0"
Expand Down Expand Up @@ -82,6 +89,14 @@ object Dependencies {
)
)

val specs2 = Def.setting {
PROVIDED(
"org.specs2" %% "specs2-core" % specs2Version.value
) ++ TEST(
"org.mockito" % "mockito-core" % mockitoVersion
)
}

val scalatestSelenium = Def.setting(
COMPILE(
"org.testcontainers" % "selenium" % testcontainersVersion,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.dimafeng.testcontainers.specs2

import com.dimafeng.testcontainers.ContainerDef
import org.specs2.specification.core.SpecificationStructure

trait TestContainerForAll extends TestContainersForAll { self: SpecificationStructure =>
val containerDef: ContainerDef
final override type Containers = containerDef.Container
override def startContainers(): containerDef.Container = containerDef.start()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.dimafeng.testcontainers.specs2

import com.dimafeng.testcontainers.ContainerDef
import org.specs2.specification.core.SpecificationStructure

trait TestContainerForEach extends TestContainersForEach { self: SpecificationStructure =>
val containerDef: ContainerDef
final override type Containers = containerDef.Container
override def startContainers(): containerDef.Container = containerDef.start()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.dimafeng.testcontainers.specs2

import com.dimafeng.testcontainers.lifecycle.Andable
import org.specs2.execute.{AsResult, Result}
import org.specs2.specification.core.SpecificationStructure
import org.specs2.specification.{AroundEach, BeforeAfterAll}

trait TestContainersForAll extends TestContainersSuite with BeforeAfterAll with AroundEach { self: SpecificationStructure =>

override def beforeAll(): Unit = {
val containers = startContainers()
startedContainers = Some(containers)
try {
afterContainersStart(containers)
} catch {
case e: Throwable =>
stopContainers(containers)
throw e
}
}

override def around[R: AsResult](r: => R): Result = {
startedContainers.foreach(beforeTest)
val result = AsResult(r)
val throwable = result match {
case f: org.specs2.execute.Failure => Some(f.exception)
case e: org.specs2.execute.Error => Some(e.exception)
case _ => None
}
startedContainers.foreach(afterTest(_, throwable))
result
}

override def afterAll(): Unit = {
startedContainers.foreach(stopContainers)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.dimafeng.testcontainers.specs2

import com.dimafeng.testcontainers.lifecycle.Andable
import org.specs2.execute.{AsResult, Result}
import org.specs2.specification.core.SpecificationStructure
import org.specs2.specification.AroundEach

trait TestContainersForEach extends TestContainersSuite with AroundEach { self: SpecificationStructure =>

override def around[R: AsResult](r: => R): Result = {
val containers = startContainers()
startedContainers = Some(containers)
try {
afterContainersStart(containers)
beforeTest(containers)

val result = AsResult(r)

val throwable = result match {
case f: org.specs2.execute.Failure => Some(f.exception)
case e: org.specs2.execute.Error => Some(e.exception)
case _ => None
}
afterTest(containers, throwable)
result
} finally {
stopContainers(containers)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.dimafeng.testcontainers.specs2

import com.dimafeng.testcontainers.implicits.DockerImageNameConverters
import com.dimafeng.testcontainers.lifecycle.{Andable, TestLifecycleAware}
import org.specs2.specification.core.SpecificationStructure
import org.junit.runner.{Description => JunitDescription}
import org.testcontainers.lifecycle.TestDescription

trait TestContainersSuite extends DockerImageNameConverters { self: SpecificationStructure =>
type Containers <: Andable

def startContainers(): Containers

def withContainers[A](runTest: Containers => A): A = {
val c = startedContainers.getOrElse(throw new IllegalStateException(
"'withContainers' method can't be used before all containers are started. " +
"'withContainers' method should be used only in test cases to prevent this."
))
runTest(c)
}

def afterContainersStart(containers: Containers): Unit = {}
def beforeContainersStop(containers: Containers): Unit = {}

@volatile private[testcontainers] var startedContainers: Option[Containers] = None

private[testcontainers] val suiteDescription: TestDescription = {
val description = JunitDescription.createSuiteDescription(self.getClass)
new TestDescription {
override def getTestId: String = description.getDisplayName
override def getFilesystemFriendlyName: String = s"${description.getClassName}-${description.getMethodName}"
}
}

private[testcontainers] def beforeTest(containers: Containers): Unit = {
containers.foreach {
case container: TestLifecycleAware => container.beforeTest(suiteDescription)
case _ => // do nothing
}
}

private[testcontainers] def afterTest(containers: Containers, throwable: Option[Throwable]): Unit = {
containers.foreach {
case container: TestLifecycleAware => container.afterTest(suiteDescription, throwable)
case _ => // do nothing
}
}

private[testcontainers] def stopContainers(containers: Containers): Unit = {
try {
beforeContainersStop(containers)
} finally {
try {
startedContainers.foreach(_.stop())
} finally {
startedContainers = None
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.dimafeng.testcontainers.specs2

import java.util.Optional
import com.dimafeng.testcontainers.{ContainerDef, SingleContainer}
import com.dimafeng.testcontainers.lifecycle.TestLifecycleAware
import org.testcontainers.containers.{GenericContainer => JavaGenericContainer}
import org.testcontainers.lifecycle.{TestDescription, TestLifecycleAware => JavaTestLifecycleAware}

case class SampleContainer(sampleJavaContainer: SampleContainer.SampleJavaContainer)
extends SingleContainer[SampleContainer.SampleJavaContainer] with TestLifecycleAware {
override implicit val container: SampleContainer.SampleJavaContainer = sampleJavaContainer

override def beforeTest(description: TestDescription): Unit = {
container.beforeTest(description)
}

override def afterTest(description: TestDescription, throwable: Option[Throwable]): Unit = {
container.afterTest(description, throwable.fold[Optional[Throwable]](Optional.empty())(Optional.of))
}
}

object SampleContainer {
class SampleJavaContainer extends JavaGenericContainer[SampleJavaContainer] with JavaTestLifecycleAware {
override def beforeTest(description: TestDescription): Unit = {}
override def afterTest(description: TestDescription, throwable: Optional[Throwable]): Unit = {}
override def start(): Unit = {}
override def stop(): Unit = {}
}

case class Def(sampleJavaContainer: SampleJavaContainer) extends ContainerDef {
override type Container = SampleContainer
override protected def createContainer(): SampleContainer = SampleContainer(sampleJavaContainer)
}
}
Loading