Intro to
Testcontainers
Key learnings
- Easy setup of complete test environments
- Not a lot effort to integration test using real external applications
- Development history of Docker Spock-Extension
$ git clone https://github.com/foo/bar.git
$ cd bar
$ ./gradlew build
How to fuse both worlds?
Spock + Docker = Spocker?
Or: How to build your own Spock extension
@Docker(image = "emilevauge/whoami", ports = @PortBinding("8080:80"))
class DockerExtensionIT extends Specification {
def "should start accessible docker container"() {
given: "a http client"
def client = HttpClientBuilder.create().build()
when: "accessing web server"
def response = client.execute(new HttpGet("http://localhost:8080"))
then: "docker container is running and returns http status code 200"
response.statusLine.statusCode == 200
}
}
First commit
class DockerMethodInterceptor extends AbstractMethodInterceptor {
private final DockerClientFacade dockerClient
DockerMethodInterceptor(Docker docker) {
this(new DockerClientFacade(docker))
}
DockerMethodInterceptor(DockerClientFacade dockerClient) {
this.dockerClient = dockerClient
}
@Override
void interceptSpecExecution(IMethodInvocation invocation) throws Throwable {
dockerClient.startContainer()
invocation.proceed()
dockerClient.stopContainer()
}
}
First commit
GenericContainer redis =
new GenericContainer("redis:3.0.2")
.withExposedPorts(6379);
redis.start();
// test my stuff
redis.stop();
- Generic Containers
- Specialized Containers (Databases, Selenium, Kafka)
- Dockerfile
- Dockerfile DSL
- Docker-Compose
- Dynamic port binding and API
- WaitStrategies
- Docker environment discovery (e.g. docker-machine, DOCKER_HOST, Docker for Mac, Docker for Windows)
- Platform independent
- Linux, macOS, Windows 10 (with NPIPE support!)
@ashleymcnamara
Works on Windows too!
Testcontainers 1.9.1
Testcontainers 1.10.1
JDK11 Compatible!
Annotations a good idea?
@Docker(image = "postgres", ports = ["5432:5432"], env = [
@Env(key = "POSTGRES_USER", value = "foo"),
@Env(key = "POSTGRES_PASSWORD", value = "secret")
],
waitStrategy = { new JdbcWaitStrategy(username: "foo",
password: "secret",
jdbcUrl: "jdbc:postgresql:foo",
driverClassName: "org.postgresql.Driver",
testQueryString: "SELECT 1") })
def "waits until postgres accepts jdbc connections"() {
// tests
}
Testcontainers all in!
@Testcontainers
class TestContainersClassIT extends Specification {
@Shared
GenericContainer genericContainer = new GenericContainer("postgres:latest")
.withExposedPorts(5432)
.withEnv([
POSTGRES_USER: "foo"
POSTGRES_PASSWORD: "secret"
])
}
// Set up a redis container
@ClassRule
public static GenericContainer redis =
new GenericContainer("redis:3.0.2")
.withExposedPorts(6379);
Testcontainers-Jupiter (JUnit5)
@Testcontainers
class SomeTest {
@Container
private MySQLContainer mySQLContainer = new MySQLContainer();
@Test
void someTestMethod() {
String url = mySQLContainer.getJdbcUrl();
// create a connection and run test as normal
}
}
CI inside containers?
Docker in Docker - DinD
Docker Wormhole
Docker Wormhole
Future
- Testcontainers 2.0
- Container-Core
- Test-Framework agnostic
- OO abstraction of Docker containers
- Other languages
- .NET, Go, Rust and JavaScript already exist (moved to Testcontainers org on Github)
- Windows Containers on Windows (WCOW) support
- Build and test inside containers with full IDE support
Resources
Intro to Testcontainers
By Kevin Wittek
Intro to Testcontainers
- 1,355