
Scala wrapper for testcontainers-java that
allows using docker containers for functional/integration/unit testing.
TestContainers is a Java 8 library that supports JUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container.
Testcontainers-scala in action: http://dimafeng.com/2016/08/01/testcontainers-selenium/
For scalatest users:
libraryDependencies += "com.dimafeng" %% "testcontainers-scala-scalatest" % testcontainersScalaVersion % "test"
First of all, let's add scalatest and MySQL testcontainers modules in the build.sbt file to play with:
libraryDependencies ++= Seq(
"com.dimafeng" %% "testcontainers-scala-scalatest" % testcontainersScalaVersion % "test",
"com.dimafeng" %% "testcontainers-scala-mysql" % testcontainersScalaVersion % "test",
)
There are two ScalaTest aware traits:
ForEachTestContainer starts a new container(s) before each test case and then stops and removes it.ForAllTestContainer starts and stops a container only once for all test cases within the spec.To start using it, you just need to extend one of those traits and override a container val as follows:
import com.dimafeng.testcontainers.{ForAllTestContainer, MySQLContainer}
class MysqlSpec extends FlatSpec with ForAllTestContainer {
override val container = MySQLContainer()
it should "do something" in {
Class.forName(container.driverClassName)
val connection = DriverManager.getConnection(container.jdbcUrl, container.username, container.password)
...
}
}
This spec has a clean mysql database instance for each of its test cases.
import org.testcontainers.containers.MySQLContainer
class MysqlSpec extends FlatSpec with ForAllTestContainer {
override val container = MySQLContainer()
it should "do something" in {
...
}
it should "do something 2" in {
...
}
}
This spec starts one container and both tests share the container's state.
Testcontainers-scala is modular. All modules has the same version. To depend on some module put this into your build.sbt file:
libraryDependencies += "com.dimafeng" %% moduleName % testcontainersScalaVersion % "test"
Here is the full list of the currently available modules:
testcontainers-scala-core — core module.testcontainers-scala-scalatest — Scalatest integration module.testcontainers-scala-scalatest-selenium — module to use the Selenium container with the Scalatest.testcontainers-scala-mysql — module with the MySQL container.testcontainers-scala-postgresql — module with the PostgreSQL container.testcontainers-scala-cassandra — module with the Cassandra container.testcontainers-scala-kafka — module with the Kafka container.testcontainers-scala-vault — module with the Vault container.testcontainers-scala-oracle-xe — module with the Oracle container.testcontainers-scala-neo4j — module with the Neo4J server container.testcontainers-scala-mssqlserver — module with the MsSQL server container.testcontainers-scala-clickhouse — module with the ClickHouse container.testcontainers-scala-cockroachdb — module with the CockroachDB container.testcontainers-scala-couchbase — module with the Couchbase container.testcontainers-scala-db2 — module with the DB2 container.testcontainers-scala-dynalite — module with the Dynalite container.testcontainers-scala-elasticsearch — module with the Elastic search container.testcontainers-scala-influxdb — module with the InfluxDB container.testcontainers-scala-localstack — module with the Localstack container.testcontainers-scala-mariadb — module with the MariaDB container.testcontainers-scala-mockserver — module with the MockServer container.testcontainers-scala-nginx — module with the Nginx container.testcontainers-scala-pulsar — module with the Pulsar container.testcontainers-scala-rabbitmq — module with the RabbitMQ container.testcontainers-scala-toxiproxy — module with the ToxiProxy container.testcontainers-scala-orientdb — module with the OrientDB container.testcontainers-scala-presto — module with the Presto container.Most of the modules are just proxies to the testcontainers-java modules and behave exactly like java containers.
You can find documentation about them in the testcontainers-java docs pages.
The most flexible but less convenient container type is GenericContainer. This container allows to launch any docker image
with custom configuration.
class GenericContainerSpec extends FlatSpec with ForAllTestContainer {
override val container = GenericContainer("nginx:latest",
exposedPorts = Seq(80),
waitStrategy = Wait.forHttp("/")
)
"GenericContainer" should "start nginx and expose 80 port" in {
assert(Source.fromInputStream(
new URL(s"http://${container.containerIpAddress}:${container.mappedPort(80)}/").openConnection().getInputStream
).mkString.contains("If you see this page, the nginx web server is successfully installed"))
}
}
class ComposeSpec extends FlatSpec with ForAllTestContainer {
override val container = DockerComposeContainer(new File("src/test/resources/docker-compose.yml"), exposedServices = Seq(ExposedService("redis_1", 6379)))
"DockerComposeContainer" should "retrieve non-0 port for any of services" in {
assert(container.getServicePort("redis_1", 6379) > 0)
}
}
Before you can use this type of containers, you need to add the following dependencies to your project:
"com.dimafeng" %% "testcontainers-scala-scalatest-selenium" % testcontainersScalaVersion % "test"
Now you can write a test in this way:
class SeleniumSpec extends FlatSpec with SeleniumTestContainerSuite with WebBrowser {
override def desiredCapabilities = DesiredCapabilities.chrome()
"Browser" should "show google" in {
go to "http://google.com"
}
}
In this case, you'll obtain a clean instance of browser (firefox/chrome) within container to which
a test will connect via remote-driver. See Webdriver Containers
for more details.
Requires you to add this dependency:
"com.dimafeng" %% "testcontainers-scala-mysql" % testcontainersScalaVersion % "test"
class MysqlSpec extends FlatSpec with ForAllTestContainer {
override val container = MySQLContainer()
"Mysql container" should "be started" in {
Class.forName(container.driverClassName)
val connection = DriverManager.getConnection(container.jdbcUrl, container.username, container.password)
...
}
}
The container can also be customized using the constructor parameters, this snippet will initialize a docker container from a specific docker image, with a specific schema name and specific username/password
class MysqlSpec extends FlatSpec with ForAllTestContainer {
override val container = MySQLContainer(mysqlImageVersion = "mysql:5.7.18",
databaseName = "testcontainer-scala",
username = "scala",
password = "scala")
"Mysql container" should "be started" in {
Class.forName(container.driverClassName)
val connection = DriverManager.getConnection(container.jdbcUrl, container.username, container.password)
...
}
}
Requires you to add this dependency:
"com.dimafeng" %% "testcontainers-scala-postgresql" % testcontainersScalaVersion % "test"
class PostgresqlSpec extends FlatSpec with ForAllTestContainer {
override val container = PostgreSQLContainer()
"PostgreSQL container" should "be started" in {
Class.forName(container.driverClassName)
val connection = DriverManager.getConnection(container.jdbcUrl, container.username, container.password)
...
}
}
If you need to test more than one container in your test, you could use MultipleContainers for that. Just define your containers and pass them to the MultipleContainers constructor:
val mySqlContainer = MySQLContainer()
val genericContainer = GenericContainer(...)
override val container = MultipleContainers(mySqlContainer, genericContainer)
If configuration of one container depends on runtime state of another one, you should define your containers as lazy:
lazy val container1 = Container1()
lazy val container2 = Container2(container1.port)
override val container = MultipleContainers(container1, container2)
This container will allow you to map container ports to statically defined ports on the docker host.
...
val container = FixedHostPortGenericContainer("nginx:latest",
waitStrategy = Wait.forHttp("/"),
exposedHostPort = 8090,
exposedContainerPort = 80
)
All container types have constructor methods with most popular parameters. In case you're missing some custom option from testcontainers-java, there is
a method that provides an elegant way to tune the nested container. It's not recommended to access inner container directly.
override val container = MySQLContainer().configure { c =>
c.withNetwork(...)
c.withStartupAttempts(...)
}
If you want to execute your code after container start or before container stop you can override afterStart() and beforeStop() methods.
class MysqlSpec extends FlatSpec with ForAllTestContainer {
...
override def beforeStop(): Unit = {
// your code
}
override def afterStart(): Unit = {
// your code
}
}
Starting from 0.34.0 version testcontainers-scala provides the new API.
The main motivation points are in the pull request.
This API is experimental and may change!
Container and ContainerDefDocker containers are represented through the two different entities:
ContainerDef — it's container definition. ContainerDef describes, how to build a container.ContainerDef receives some parameters.ContainerDef has a start() method. It returns a started Container.Container — it's a started container. You can interact with it through its methods.MySQLContainer you can get it's JDBC URL with jdbcUrl method.Container is the main entity for using inside tests.You can use one of the four traits:
TestContainerForAll — will start a single container before all tests and stop after all tests.TestContainerForEach — will start a single container before each test and stop after each test.TestContainersForAll — will start multiple containers before all tests and stop after all tests.TestContainersForEach — will start multiple containers before each test and stop after each test.If you want to use a single container in your test:
class MysqlSpec extends FlatSpec with TestContainerForAll {
// You need to override `containerDef` with needed container definition
override val containerDef = MySQLContainer.Def()
// To use containers in tests you need to use `withContainers` function
it should "test" in withContainers { mysqlContainer =>
// Inside your test body you can do with your container whatever you want to
assert(mysqlContainer.jdbcUrl.nonEmpty)
}
}
Usage of TestContainerForEach is not different from the example above.
If you want to use multiple containers in your test:
class ExampleSpec extends FlatSpec with TestContainersForAll {
// First of all, you need to declare, which containers you want to use
override type Containers = MySQLContainer and PostgreSQLContainer
// After that, you need to describe, how you want to start them,
// In this method you can use any intermediate logic.
// You can pass parameters between containers, for example.
override def startContainers(): Containers = {
val container1 = MySQLContainer.Def().start()
val container2 = PostgreSQLContainer.Def().start()
container1 and container2
}
// `withContainers` function supports multiple containers:
it should "test" in withContainers { case mysqlContainer and pgContainer =>
// Inside your test body you can do with your containers whatever you want to
assert(mysqlContainer.jdbcUrl.nonEmpty && pgContainer.jdbcUrl.nonEmpty)
}
}
Usage of TestContainersForEach is not different from the example above.
GenericContainer usageTo create a custom container, which is not built-in in the library, you need to use GenericContainer.
For example, you want to create a custom nginx container:
class NginxContainer(port: Int, underlying: GenericContainer) extends GenericContainer(underlying) {
// you can add any methods or fields inside your container's body
def rootUrl: String = s"http://$containerIpAddress:${mappedPort(port)}/"
}
object NginxContainer {
// In the container definition you need to describe, how your container will be constructed:
case class Def(port: Int) extends GenericContainer.Def[NginxContainer](
new NginxContainer(port, GenericContainer(
dockerImage = "nginx:latest",
exposedPorts = Seq(port),
waitStrategy = Wait.forHttp("/")
))
)
}
GenericContainer, add ContainerDef in the companion like this:object MyCustomContainer {
case class Def(/*constructor params here*/) extends GenericContainer.Def[MyCustomContainer](
new MyCustomContainer(/*constructor params here*/)
)
}
ForEachTestContainer:ForEachTestContainer with TestContainerForEachForEachTestContainer with TestContainersForEachForAllTestContainer:ForAllTestContainer with TestContainerForAllForAllTestContainer with TestContainersForAllIf you have any questions or difficulties feel free to ask it in our slack channel.
0.36.1
.waitingFor() to DockerComposeContainer0.36.0
OrientDBContainer.PrestoContainer.DockerComposeContainer.getContainerByServiceName method.dbPassword parameter from the ClickHouseContainer. Looks like this parameter was added accidentally (java container doesn't support it).0.35.2
SingleContainer:execInContainercopyFileToContainercopyFileFromContainer0.35.1
0.35.0
From this release testcontainers-scala supports all testcontainers-java containers and methods. If you find missing parts — don't hesitate to create an issue!
testcontainers-scala-neo4jtestcontainers-scala-mssqlservertestcontainers-scala-clickhousetestcontainers-scala-cockroachdbtestcontainers-scala-couchbasetestcontainers-scala-db2testcontainers-scala-dynalitetestcontainers-scala-elasticsearchtestcontainers-scala-influxdbtestcontainers-scala-localstacktestcontainers-scala-mariadbtestcontainers-scala-mockservertestcontainers-scala-nginxtestcontainers-scala-pulsartestcontainers-scala-rabbitmqtestcontainers-scala-toxiproxy SingleContainer:envMapboundPortNumberscopyToFileContainerPathMaplabelsshmSizetestHostIpAddresstmpFsMappinglogslivenessCheckPortNumbersGenericContainer constructor:labelstmpFsMappingimagePullPolicyCassandraContainer:clusterusernamepassword0.34.3
DockerComposeContainer: added DockerComposeContainer.Def.0.34.2
OracleContainer. It is in the testcontainers-scala-oracle-xe package.0.34.1
def withContainers(runTest: Containers => Unit): Unit todef withContainers[A](runTest: Containers => A): AafterStart to afterContainersStart and added a containers: Containers argument to it.beforeStop to beforeContainersStop and added a containers: Containers argument to it.0.34.0
testcontainers-scala is still provided but will be eventually dropped in future.testcontainers-scala dependency0.33.0
1.12.1 -> 1.12.20.32.0
1.12.11.3.00.31.0
0.30.0
1.12.02.12.90.29.0
1.11.2 -> 1.11.40.28.0
VaultContainer0.27.0
TestLifecycleAware trait introduced. You can use it when you want to do something with the container before or after the test.Container now implements Startable interface with start and stop methods.finished, succeeded, starting, failed are deprecated. Use start, stop, and TestLifecycleAware methods instead.KafkaContainerCassandraContainer0.26.0
1.11.2 -> 1.11.30.25.0
1.11.1 -> 1.11.20.24.0
1.10.6 -> 1.11.10.23.0
1.10.1 -> 1.10.60.22.0
1.9.1 -> 1.10.10.21.0
1.8.3 -> 1.9.10.20.0
1.8.0 -> 1.8.30.19.0
1.7.3 -> 1.8.0DockerComposeContainer enhancementsGenericContainer0.18.0
1.7.1 -> 1.7.30.17.0
1.6.0 -> 1.7.1shapeless dependencyLazyContainer. This gives you a possibility to not wrap your containers into the LazyContainer manually.MultipleContainers.apply now receives LazyContainer[_]* type. Together with the previous point, it makes usage experience of MultipleContainers more smooth.configure method. See this for more details0.16.0
FixedHostPortGenericContainer added0.15.0
MySQLContainerMultipleContainers - container lazy creation for dependent containers0.14.0
1.5.1 -> 1.6.00.13.0
1.4.3 -> 1.5.10.12.0
afterStart hook now handles exceptions correctly0.11.0
ForAllTestContainer if all tests are ignored0.10.0
1.4.2 -> 1.4.30.8.0
0.7.0
1.2.1 -> 1.4.20.6.0
1.2.0 -> 1.2.1afterStart hook0.5.0
1.1.8 -> 1.2.00.4.1
1.1.7 -> 1.1.80.4.0
1.1.5 -> 1.1.70.3.0
1.1.0 -> 1.1.50.2.0
1.0.5 -> 1.1.0$ sbt clean release