Skip to content

Commit

Permalink
junit5 DockerComposeExtension should be a @ClassRule equivalent (also…
Browse files Browse the repository at this point in the history
… docs) (#332)

DockerComposeExtension implements BeforeAllCallback, to ensure containers are started up once at the beginning of the test class
  • Loading branch information
iamdanfox authored and bulldozer-bot[bot] committed Aug 22, 2019
1 parent d2f7d31 commit 2bcb282
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 154 deletions.
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,32 @@ This will cause the containers defined in `src/test/resources/docker-compose.yml

The `docker-compose.yml` file is referenced using the path given, relative to the working directory of the test. It will not be copied elsewhere and so references to shared directories and other resources for your containers can be made using path relative to this file as normal. If you wish to manually run the Docker containers for debugging the tests simply run `docker-compose up` in the same directory as the `docker-compose.yml`.

### JUnit 5

If you'd prefer to use JUnit 5 (aka JUnit Jupiter), use the following dependency and replace your usages of `DockerComposeRule` with `DockerComposeExtension`.

```gradle
dependencies {
testCompile 'com.palantir.docker.compose:docker-compose-junit-jupiter:<latest-tag-from-bintray>'
}
```

```java
public class MyIntegrationTest {

@RegisterExtension
public static DockerComposeExtension docker = DockerComposeExtension.builder()
.file("src/test/resources/docker-compose.yml")
.build();

@Test
public void testThatUsesSomeDockerServices() throws InterruptedException, IOException {
...
}

}
```

### Running on a Mac

The above example will work out of the box on Linux machines with Docker installed. On Mac you will first need to install Docker using the instructions [here](https://docs.docker.com/v1.8/installation/mac/).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,169 +16,37 @@

package com.palantir.docker.compose;

import static com.google.common.base.Throwables.propagate;
import static com.palantir.docker.compose.connection.waiting.HealthChecks.toHaveAllPortsOpen;
import static com.palantir.docker.compose.execution.DockerComposeExecArgument.arguments;
import static com.palantir.docker.compose.execution.DockerComposeExecOption.options;
import static org.assertj.core.api.Assertions.assertThat;

import com.google.common.collect.ImmutableList;
import com.palantir.docker.compose.configuration.DockerComposeFiles;
import com.palantir.docker.compose.connection.Container;
import com.palantir.docker.compose.connection.State;
import com.palantir.docker.compose.connection.waiting.HealthCheck;
import com.palantir.docker.compose.connection.waiting.SuccessOrFailure;
import com.palantir.docker.compose.execution.DockerComposeRunArgument;
import com.palantir.docker.compose.execution.DockerComposeRunOption;
import java.io.IOException;
import java.util.List;
import java.util.function.Consumer;
import org.junit.jupiter.api.Disabled;
import com.palantir.docker.compose.connection.waiting.HealthChecks;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

// Keep in sync with DockerComposeRuleIntegrationTest
public class DockerComposeExtensionIntegrationTest {

private static final List<String> CONTAINERS = ImmutableList.of("db", "db2", "db3", "db4");

@RegisterExtension
public final DockerComposeExtension docker = DockerComposeExtension.builder()
.files(DockerComposeFiles.from("src/integrationTest/resources/docker-compose.yaml"))
.waitingForService("db", toHaveAllPortsOpen())
.waitingForService("db2", toHaveAllPortsOpen())
.waitingForServices(ImmutableList.of("db3", "db4"), toAllHaveAllPortsOpen())
.build();

private static HealthCheck<List<Container>> toAllHaveAllPortsOpen() {
return containers -> {
boolean healthy = containers.stream()
.map(Container::areAllPortsOpen)
.allMatch(SuccessOrFailure::succeeded);

return SuccessOrFailure.fromBoolean(healthy, "");
};
}

private static void forEachContainer(Consumer<String> consumer) {
CONTAINERS.forEach(consumer);
}

@Test
public void should_run_docker_compose_up_using_the_specified_docker_compose_file_to_bring_postgres_up() {
forEachContainer(container -> {
assertThat(docker.containers().container(container).port(5432).isListeningNow()).isTrue();
});
}

@Test
public void after_test_is_executed_the_launched_postgres_container_is_no_longer_listening() {
docker.after();

forEachContainer(container -> {
assertThat(docker.containers().container(container).port(5432).isListeningNow()).isFalse();
});
}

@Test
public void can_access_external_port_for_internal_port_of_machine() {
forEachContainer(container -> {
assertThat(docker.containers().container(container).port(5432).isListeningNow()).isTrue();
});
}

@Test
public void can_stop_and_start_containers() {
forEachContainer(containerName -> {
try {
Container container = docker.containers().container(containerName);
public static final DockerComposeExtension docker = DockerComposeExtension.builder()
.files(DockerComposeFiles.from("src/integrationTest/resources/docker-compose.yaml"))
.waitingForService("db", HealthChecks.toHaveAllPortsOpen())
.waitingForService("db2", HealthChecks.toHaveAllPortsOpen())
.waitingForService("db3", HealthChecks.toHaveAllPortsOpen())
.waitingForService("db4", HealthChecks.toHaveAllPortsOpen())
.build();

container.stop();
assertThat(container.state()).isEqualTo(State.DOWN);

container.start();
assertThat(container.state()).isEqualTo(State.HEALTHY);
} catch (IOException | InterruptedException e) {
propagate(e);
}
});
}

@Test
public void stop_can_be_run_on_stopped_container() {
forEachContainer(containerName -> {
try {
Container container = docker.containers().container(containerName);

container.stop();
assertThat(container.state()).isEqualTo(State.DOWN);

container.stop();
} catch (IOException | InterruptedException e) {
propagate(e);
}
});
}

@Test
public void start_can_be_run_on_running_container() {
forEachContainer(containerName -> {
try {
Container container = docker.containers().container(containerName);

container.start();
assertThat(container.state()).isEqualTo(State.HEALTHY);
} catch (IOException | InterruptedException e) {
propagate(e);
}
});
}

@Test
public void can_kill_and_start_containers() {
forEachContainer(containerName -> {
try {
Container container = docker.containers().container(containerName);

container.kill();
assertThat(container.state()).isEqualTo(State.DOWN);

container.start();
assertThat(container.state()).isEqualTo(State.HEALTHY);
} catch (IOException | InterruptedException e) {
propagate(e);
}
});
}

@Test
public void kill_can_be_run_on_killed_container() {
forEachContainer(containerName -> {
try {
Container container = docker.containers().container(containerName);

container.kill();
assertThat(container.state()).isEqualTo(State.DOWN);

container.kill();
} catch (IOException | InterruptedException e) {
propagate(e);
}
});
}
private static int port;

@Test
@Disabled("This test will not run on Circle CI because it does not currently support docker-compose exec.")
public void exec_returns_output() throws Exception {
assertThat(docker.exec(options(), CONTAINERS.get(0), arguments("echo", "hello"))).isEqualTo("hello");
@Order(1)
public void an_external_port_exists() {
port = docker.containers().container("db").port(5432).getExternalPort();
assertThat(port).isNotZero();
}

@Test
public void run_returns_output() throws Exception {
String actual = docker.run(
DockerComposeRunOption.options("--entrypoint", "echo"),
CONTAINERS.get(0),
DockerComposeRunArgument.arguments("hello"));
assertThat(actual).isEqualTo("hello");
@Order(2)
public void container_stays_up_between_tests() {
assertThat(docker.containers().container("db").port(5432).getExternalPort()).isEqualTo(port);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,27 @@

package com.palantir.docker.compose;

import java.io.IOException;
import org.immutables.value.Value;
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.AfterAllCallback;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;

/**
* A JUnit 5 extension to bring up Docker containers defined in a docker-compose.yml before running tests.
*/
@Value.Immutable
@CustomImmutablesStyle
public abstract class DockerComposeExtension extends DockerComposeManager
implements BeforeEachCallback, AfterEachCallback {
implements BeforeAllCallback, AfterAllCallback {

@Override
public void beforeEach(ExtensionContext context) throws Exception {
public void beforeAll(ExtensionContext unused) throws IOException, InterruptedException {
before();
}

@Override
public void afterEach(ExtensionContext context) throws Exception {
public void afterAll(ExtensionContext unused) {
after();
}

Expand Down

0 comments on commit 2bcb282

Please sign in to comment.