com.dimafeng.testcontainers.munit.TestContainersSuite.scala Maven / Gradle / Ivy
The newest version!
package com.dimafeng.testcontainers.munit
import com.dimafeng.testcontainers.implicits.DockerImageNameConverters
import com.dimafeng.testcontainers.lifecycle.{Andable, TestLifecycleAware}
import com.dimafeng.testcontainers.munit.TestContainersSuite.IllegalWithContainersCall
import munit.Suite
import org.junit.runner.{Description => JunitDescription}
import org.testcontainers.lifecycle.TestDescription
trait TestContainersSuite extends DockerImageNameConverters { self: Suite =>
/**
* To use testcontainers suites you need to declare,
* which containers you want to use inside your tests.
*
* For example:
* {{{
* override type Containers = MySQLContainer
* }}}
*
* If you want to use multiple containers inside your tests, use `and` syntax:
* {{{
* override type Containers = MySQLContainer and PostgreSQLContainer
* }}}
*/
type Containers <: Andable
/**
* Contains containers startup logic.
* In this method you can use any intermediate logic.
* You can pass parameters between containers, for example:
* {{{
* override def startContainers(): Containers = {
* val container1 = Container1.Def().start()
* val container2 = Container2.Def(container1.someParam).start()
* container1 and container2
* }
* }}}
*
* @return Started containers
*/
def startContainers(): Containers
/**
* To use containers inside your test bodies you need to use `withContainers` function:
* {{{
* test("test") {
* withContainers { mysqlContainer =>
* // Inside your test body you can do with your container whatever you want to
* assert(mysqlContainer.jdbcUrl.nonEmpty)
* }
* }
* }}}
*
* `withContainers` also supports multiple containers:
* {{{
* test("test") {
* withContainers { case mysqlContainer and pgContainer =>
* // test body
* }
* }
* }}}
*
* @param runTest Test body
*/
def withContainers[A](runTest: Containers => A): A = {
val c = startedContainers.getOrElse(throw IllegalWithContainersCall())
runTest(c)
}
/**
* Override, if you want to do something after containers start.
*/
def afterContainersStart(containers: Containers): Unit = {}
/**
* Override, if you want to do something before containers stop.
*/
def beforeContainersStop(containers: Containers): Unit = {}
@volatile private[testcontainers] var startedContainers: Option[Containers] = None
val suiteDescription = TestContainersSuite.createDescription(self)
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
}
}
}
}
object TestContainersSuite {
case class IllegalWithContainersCall() extends IllegalStateException(
"'withContainers' method can't be used before all containers are started. " +
"'withContainers' method should be used only in test cases to prevent this."
)
def createDescription(suite: Suite): TestDescription = {
val description = JunitDescription.createSuiteDescription(suite.getClass)
// If we don't add the testNames and nested suites in, we get
// Unrooted Tests show up in Eclipse
for (name <- suite.munitTests()) {
description.addChild(JunitDescription.createTestDescription(suite.getClass, name.name))
}
new TestDescription {
override def getTestId: String = description.getDisplayName
override def getFilesystemFriendlyName: String = s"${description.getClassName}-${description.getMethodName}"
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy