From 0965d94e2e27b2df045458147b495b48070c15a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Thu, 27 Feb 2025 18:28:41 +0100 Subject: [PATCH 1/5] chore: add testcontainers entry in the OSS section of Manuals --- content/manuals/_index.md | 4 ++++ static/assets/icons/Testcontainers.svg | 6 ++++++ 2 files changed, 10 insertions(+) create mode 100644 static/assets/icons/Testcontainers.svg diff --git a/content/manuals/_index.md b/content/manuals/_index.md index 7aea7a82fa9..630d92371d0 100644 --- a/content/manuals/_index.md +++ b/content/manuals/_index.md @@ -26,6 +26,10 @@ params: description: Define and run multi-container applications. icon: /assets/icons/Compose.svg link: /compose/ + - title: Testcontainers OSS + description: Run containers programmatically in your preferred programming language. + icon: /assets/icons/Testcontainers.svg + link: /testcontainers/ products: - title: Docker Desktop description: Your command center for container development. diff --git a/static/assets/icons/Testcontainers.svg b/static/assets/icons/Testcontainers.svg new file mode 100644 index 00000000000..5d2d59fece2 --- /dev/null +++ b/static/assets/icons/Testcontainers.svg @@ -0,0 +1,6 @@ + + + + + + From 56151458503a7955e96d12edbc224f34dcc821fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Thu, 27 Feb 2025 18:36:10 +0100 Subject: [PATCH 2/5] chore: adds quickstarts for tc-go and tc-java --- content/manuals/testcontainers/_index.md | 30 ++ content/manuals/testcontainers/benefits.md | 41 ++ .../testcontainers/getting-started/_index.md | 35 ++ .../testcontainers/getting-started/go.md | 133 +++++ .../testcontainers/getting-started/java.md | 468 ++++++++++++++++++ 5 files changed, 707 insertions(+) create mode 100644 content/manuals/testcontainers/_index.md create mode 100644 content/manuals/testcontainers/benefits.md create mode 100644 content/manuals/testcontainers/getting-started/_index.md create mode 100644 content/manuals/testcontainers/getting-started/go.md create mode 100644 content/manuals/testcontainers/getting-started/java.md diff --git a/content/manuals/testcontainers/_index.md b/content/manuals/testcontainers/_index.md new file mode 100644 index 00000000000..598947d0834 --- /dev/null +++ b/content/manuals/testcontainers/_index.md @@ -0,0 +1,30 @@ +--- +title: Testcontainers OSS +weight: 30 +description: Learn how to use Testcontainers OSS to run containers programmatically in your preferred programming language. +keywords: docker APIs, docker, testcontainers documentation, testcontainers, testcontainers oss, testcontainers oss documentation, + docker compose, docker-compose, java, golang, go +params: + sidebar: + group: Open source +grid: +- title: Why use Testcontainers? + description: Understand Testcontainers' key benefits + icon: feature_search + link: /testcontainers/benefits/ +- title: Quickstart + description: Learn the key concepts of Testcontainers OSS + icon: explore + link: /testcontainers/getting-started +aliases: +- /testcontainers/ +--- + +Testcontainers is set libraries that provides easy and lightweight APIs for bootstrapping local development and test dependencies with real services wrapped in Docker containers. +Using Testcontainers, you can write tests that depend on the same services you use in production without mocks or in-memory services. + +{{< grid >}} + +## Want to know more? + +- Visit the [Testcontainers website](https://testcontainers.com/getting-started/) diff --git a/content/manuals/testcontainers/benefits.md b/content/manuals/testcontainers/benefits.md new file mode 100644 index 00000000000..41881cd35a3 --- /dev/null +++ b/content/manuals/testcontainers/benefits.md @@ -0,0 +1,41 @@ +--- +description: Key benefits and use cases of Testcontainers OSS +keywords: documentation, docs, docker, testcontainers, containers, uses, benefits +title: Why use Testcontainers? +weight: 10 +linkTitle: Why use Testcontainers? +aliases: +- /testcontainers/benefits/ +--- + +### Benefits of using Testcontainers: + +* **On-demand isolated infrastructure provisioning:** +You don't need to have a pre-provisioned integration testing infrastructure. +Testcontainers will provide the required services before running your tests. +There will be no test data pollution, even when multiple build pipelines run in parallel +because each pipeline runs with an isolated set of services. + +* **Consistent experience on both local and CI environments:** +You can run your integration tests right from your IDE, just like you run unit tests. +No need to push your changes and wait for the CI pipeline to complete. + +* **Reliable test setup using wait strategies:** +Docker containers need to be started and fully initialized before using them in your tests. +The Testcontainers library offers several out-of-the-box wait strategies implementations to make sure +the containers (and the application within) are fully initialized. +Testcontainers modules already implement the relevant wait strategies for a given technology, +and you can always implement your own or create a composite strategy if needed. + +* **Advanced networking capabilities:** +Testcontainers libraries map the container's ports onto random ports available on the host machine +so that your tests connect reliably to those services. You can even create a (Docker) network and +connect multiple containers together so that they talk to each other via static Docker network aliases. + +* **Automatic clean up:** +The Testcontainers library takes care of removing any created resources (containers, volumes, networks etc.) +automatically after the test execution is complete by using the Ryuk sidecar container. +While starting the required containers, Testcontainers attaches a set of labels to the +created resources (containers, volumes, networks etc) and Ryuk automatically performs +resource clean up by matching those labels. +This works reliably even when the test process exits abnormally (e.g. sending a SIGKILL). diff --git a/content/manuals/testcontainers/getting-started/_index.md b/content/manuals/testcontainers/getting-started/_index.md new file mode 100644 index 00000000000..4b13fc92a08 --- /dev/null +++ b/content/manuals/testcontainers/getting-started/_index.md @@ -0,0 +1,35 @@ +--- +description: Learn how to install Testcontainers OSS and run your first container +keywords: testcontainers, testcontainers quickstart, testcontainers oss, testcontainers oss quickstart, testcontainers quickstart, + java, go, golang +title: Testcontainers Quickstart +linkTitle: Testcontainers Quickstart +weight: 20 +toc_max: 3 +aliases: +- /testcontainers/getting-started/ +--- + +This page contains summary information about Testcontainers OSS. + +## Supported languages + +Testcontainers provide support for the most popular languages, and Docker sponsors the development of the following Testcontainers implementations: + +- [Go](/manuals/testcontainers/getting-started/go.md) +- [Java](/manuals/testcontainers/getting-started/java.md) + +The rest are community-driven and maintained by independent contributors. + +## Prerequisites + +Testcontainers requires a Docker-API compatible container runtime. +During development, Testcontainers is actively tested against recent versions of Docker on Linux, as well as against Docker Desktop on Mac and Windows. +These Docker environments are automatically detected and used by Testcontainers without any additional configuration being necessary. + +It is possible to configure Testcontainers to work for other Docker setups, such as a remote Docker host or Docker alternatives. +However, these are not actively tested in the main development workflow, so not all Testcontainers features might be available +and additional manual configuration might be necessary. + +If you have further questions about configuration details for your setup or whether it supports running Testcontainers-based tests, +please contact the Testcontainers team and other users from the Testcontainers community on [Slack](https://slack.testcontainers.org/). diff --git a/content/manuals/testcontainers/getting-started/go.md b/content/manuals/testcontainers/getting-started/go.md new file mode 100644 index 00000000000..eb479cae52a --- /dev/null +++ b/content/manuals/testcontainers/getting-started/go.md @@ -0,0 +1,133 @@ +--- +description: How to install Testcontainers for Go and run your first container +keywords: testcontainers, testcontainers go, testcontainers oss, testcontainers oss go, testcontainers go quickstart +toc_max: 3 +title: Go quickstart +linkTitle: Go +aliases: +- /testcontainers/getting-started/go/ +weight: 10 +--- + +_Testcontainers for Go_ plays well with the native `go test` framework. + +The ideal use case is for integration or end to end tests. It helps you to spin +up and manage the dependencies life cycle via Docker. + +## System requirements + +### Go version + +From the [Go Release Policy](https://go.dev/doc/devel/release#policy): + +> Each major Go release is supported until there are two newer major releases. For example, Go 1.5 was supported until the Go 1.7 release, and Go 1.6 was supported until the Go 1.8 release. We fix critical problems, including critical security problems, in supported releases as needed by issuing minor revisions (for example, Go 1.6.1, Go 1.6.2, and so on). + +_Testcontainers for Go_ is tested against those two latest Go releases, therefore we recommend using any of them. + +## Step 1:Install _Testcontainers for Go_ + +_Testcontainers for Go_ uses [go mod](https://blog.golang.org/using-go-modules) and you can get it installed via: + +``` +go get github.com/testcontainers/testcontainers-go +``` + +## Step 2: Spin up Redis + +```go +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/testcontainers/testcontainers-go" + "github.com/testcontainers/testcontainers-go/wait" +) + +func TestWithRedis(t *testing.T) { + ctx := context.Background() + req := testcontainers.ContainerRequest{ + Image: "redis:latest", + ExposedPorts: []string{"6379/tcp"}, + WaitingFor: wait.ForLog("Ready to accept connections"), + } + redisC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ContainerRequest: req, + Started: true, + }) + testcontainers.CleanupContainer(t, redisC) + require.NoError(t, err) +} +``` + +The `testcontainers.ContainerRequest` describes how the Docker container will +look. + +* `Image` is the Docker image the container starts from. +* `ExposedPorts` lists the ports to be exposed from the container. +* `WaitingFor` is a field you can use to validate when a container is ready. It + is important to get this set because it helps to know when the container is + ready to receive any traffic. In this case, we check for the logs we know come + from Redis, telling us that it is ready to accept requests. + +When you use `ExposedPorts` you have to imagine yourself using `docker run -p +`. When you do so, `dockerd` maps the selected `` from inside the +container to a random one available on your host. + +In the previous example, we expose `6379` for `tcp` traffic to the outside. This +allows Redis to be reachable from your code that runs outside the container, but +it also makes parallelization possible because if you add `t.Parallel` to your +tests, and each of them starts a Redis container each of them will be exposed on a +different random port. + +`testcontainers.GenericContainer` creates the container. In this example we are +using `Started: true`. It means that the container function will wait for the +container to be up and running. If you set the `Start` value to `false` it won't +start, leaving to you the decision about when to start it. + +All the containers must be removed at some point, otherwise they will run until +the host is overloaded. One of the ways we have to clean up is by deferring the +terminated function: `defer testcontainers.TerminateContainer(redisC)` which +automatically handles nil container so is safe to use even in the error case. + +> [!TIP] +> +> Look at [features/garbage_collector](/features/garbage_collector/) to know another +> way to clean up resources. + +## Step 3: Make your code talk to the container + +This is just an example, but usually Go applications that rely on Redis are +using the [redis-go](https://github.com/go-redis/redis) client. This code gets +the endpoint from the container we just started, and it configures the client. + +```go +endpoint, err := redisC.Endpoint(ctx, "") +if err != nil { + t.Error(err) +} + +client := redis.NewClient(&redis.Options{ + Addr: endpoint, +}) + +_ = client +``` + +We expose only one port, so the `Endpoint` does not need a second argument set. + +> [!TIP] +> +> If you expose more than one port you can specify the one you need as a second +> argument. + +In this case it returns: `localhost:`. + +## Step 4: Run the test + +You can run the test via `go test ./...` + +## Step 5: Want to go deeper with Redis? + +You can find a more elaborated Redis example in our examples section. Please check it out [here](https://golang.testcontainers.org/modules/redis/). diff --git a/content/manuals/testcontainers/getting-started/java.md b/content/manuals/testcontainers/getting-started/java.md new file mode 100644 index 00000000000..f3587cccd86 --- /dev/null +++ b/content/manuals/testcontainers/getting-started/java.md @@ -0,0 +1,468 @@ +--- +title: Java quickstart +linkTitle: Java +description: How to install Testcontainers for Java and run your first container +keywords: testcontainers, testcontainers java, testcontainers oss, testcontainers oss java, testcontainers java quickstart, + junit, junit4, junit5, junit 4, junit 5 +toc_max: 3 +weight: 20 +--- + +- [JUnit 4 Quickstart](#junit-4-quickstart) +- [JUnit 5 Quickstart](#junit-5-quickstart) +- [Spock Quickstart](#spock-quickstart) + +## JUnit 4 Quickstart + +It's easy to add Testcontainers to your project - let's walk through a quick example to see how. + +Let's imagine we have a simple program that has a dependency on Redis, and we want to add some tests for it. +In our imaginary program, there is a `RedisBackedCache` class which stores data in Redis. + +You can see an example test that could have been written for it (without using Testcontainers): + +```java +public class RedisBackedCacheIntTestStep0 { + + private RedisBackedCache underTest; + + @Before + public void setUp() { + // Assume that we have Redis running locally? + underTest = new RedisBackedCache("localhost", 6379); + } + + @Test + public void testSimplePutAndGet() { + underTest.put("test", "example"); + + String retrieved = underTest.get("test"); + assertThat(retrieved).isEqualTo("example"); + } +} +``` + +Notice that the existing test has a problem - it's relying on a local installation of Redis, which is a red flag for test reliability. +This may work if we were sure that every developer and CI machine had Redis installed, but would fail otherwise. +We might also have problems if we attempted to run tests in parallel, such as state bleeding between tests, or port clashes. + +Let's start from here, and see how to improve the test with Testcontainers: + +### Step 1: Add Testcontainers as a test-scoped dependency + +First, add Testcontainers as a dependency as follows: + +{{< tabs group="lang" >}} +{{< tab name="Gradle" >}} + +```groovy +testImplementation "org.testcontainers:testcontainers:{{latest_version}}" +``` + +{{< /tab >}} +{{< tab name="Maven" >}} + +```xml + + org.testcontainers + testcontainers + {{latest_version}} + test + +``` + +{{< /tab >}} +{{< /tabs >}} + +### Step 2: Get Testcontainers to run a Redis container during our tests + +Simply add the following to the body of our test class: + +```java +@Rule +public GenericContainer redis = new GenericContainer(DockerImageName.parse("redis:6-alpine")) + .withExposedPorts(6379); +``` + +The `@Rule` annotation tells JUnit to notify this field about various events in the test lifecycle. +In this case, our rule object is a Testcontainers `GenericContainer`, configured to use a specific Redis image from Docker Hub, and configured to expose a port. + +If we run our test as-is, then regardless of the actual test outcome, we'll see logs showing us that Testcontainers: + +* was activated before our test method ran +* discovered and quickly tested our local Docker setup +* pulled the image if necessary +* started a new container and waited for it to be ready +* shut down and deleted the container after the test + +### Step 3: Make sure our code can talk to the container + +Before Testcontainers, we might have hardcoded an address like `localhost:6379` into our tests. + +Testcontainers uses *randomized ports* for each container it starts, but makes it easy to obtain the actual port at runtime. +We can do this in our test `setUp` method, to set up our component under test: + +```java +String address = redis.getHost(); +Integer port = redis.getFirstMappedPort(); + +// Now we have an address and port for Redis, no matter where it is running +underTest = new RedisBackedCache(address, port); +``` + +> [!TIP] +> +> Notice that we also ask Testcontainers for the container's actual address with `redis.getHost();`, +> rather than hard-coding `localhost`. `localhost` may work in some environments but not others - for example it may +> not work on your current or future CI environment. As such, **avoid hard-coding** the address, and use +> `getHost()` instead. + +### Step 4: Run the tests! + +That's it! + +Let's look at our complete test class to see how little we had to add to get up and running with Testcontainers: + +```java +public class RedisBackedCacheIntTest { + + private RedisBackedCache underTest; + + // rule { + @Rule + public GenericContainer redis = new GenericContainer(DockerImageName.parse("redis:6-alpine")) + .withExposedPorts(6379); + + // } + + @Before + public void setUp() { + String address = redis.getHost(); + Integer port = redis.getFirstMappedPort(); + + // Now we have an address and port for Redis, no matter where it is running + underTest = new RedisBackedCache(address, port); + } + + @Test + public void testSimplePutAndGet() { + underTest.put("test", "example"); + + String retrieved = underTest.get("test"); + assertThat(retrieved).isEqualTo("example"); + } +} +``` + +## JUnit 5 Quickstart + +It's easy to add Testcontainers to your project - let's walk through a quick example to see how. + +Let's imagine we have a simple program that has a dependency on Redis, and we want to add some tests for it. +In our imaginary program, there is a `RedisBackedCache` class which stores data in Redis. + +You can see an example test that could have been written for it (without using Testcontainers): + +```java +public class RedisBackedCacheIntTestStep0 { + + private RedisBackedCache underTest; + + @BeforeEach + public void setUp() { + // Assume that we have Redis running locally? + underTest = new RedisBackedCache("localhost", 6379); + } + + @Test + public void testSimplePutAndGet() { + underTest.put("test", "example"); + + String retrieved = underTest.get("test"); + assertThat(retrieved).isEqualTo("example"); + } +} +``` + +Notice that the existing test has a problem - it's relying on a local installation of Redis, which is a red flag for test reliability. +This may work if we were sure that every developer and CI machine had Redis installed, but would fail otherwise. +We might also have problems if we attempted to run tests in parallel, such as state bleeding between tests, or port clashes. + +Let's start from here, and see how to improve the test with Testcontainers: + +### Step 1: Add Testcontainers as a test-scoped dependency + +First, add Testcontainers as a dependency as follows: + +{{< tabs group="lang" >}} +{{< tab name="Gradle" >}} + +```groovy +testImplementation "org.junit.jupiter:junit-jupiter:5.8.1" +testImplementation "org.testcontainers:testcontainers:{{latest_version}}" +testImplementation "org.testcontainers:junit-jupiter:{{latest_version}}" +``` + +{{< /tab >}} +{{< tab name="Maven" >}} + +```xml + + org.junit.jupiter + junit-jupiter + 5.8.1 + test + + + org.testcontainers + testcontainers + {{latest_version}} + test + + + org.testcontainers + junit-jupiter + {{latest_version}} + test + +``` + +{{< /tab >}} +{{< /tabs >}} + +### Step 2: Get Testcontainers to run a Redis container during our tests + +First, you'll need to annotate the test class with `@Testcontainers`. Furthermore, add the following to the body of our test class: + +```java +@Container +public GenericContainer redis = new GenericContainer(DockerImageName.parse("redis:6-alpine")) + .withExposedPorts(6379); +``` + +The `@Container` annotation tells JUnit to notify this field about various events in the test lifecycle. +In this case, our rule object is a Testcontainers `GenericContainer`, configured to use a specific Redis image from Docker Hub, and configured to expose a port. + +If we run our test as-is, then regardless of the actual test outcome, we'll see logs showing us that Testcontainers: + +* was activated before our test method ran +* discovered and quickly tested our local Docker setup +* pulled the image if necessary +* started a new container and waited for it to be ready +* shut down and deleted the container after the test + +### Step 3: Make sure our code can talk to the container + +Before Testcontainers, we might have hardcoded an address like `localhost:6379` into our tests. + +Testcontainers uses *randomized ports* for each container it starts, but makes it easy to obtain the actual port at runtime. +We can do this in our test `setUp` method, to set up our component under test: + +```java +String address = redis.getHost(); +Integer port = redis.getFirstMappedPort(); + +// Now we have an address and port for Redis, no matter where it is running +underTest = new RedisBackedCache(address, port); +``` + +> [!TIP] +> +> Notice that we also ask Testcontainers for the container's actual address with `redis.getHost();`, +> rather than hard-coding `localhost`. `localhost` may work in some environments but not others - for example it may +> not work on your current or future CI environment. As such, **avoid hard-coding** the address, and use +> `getHost()` instead. + +### Step 4: Additional attributes + +Additional attributes are available for the `@Testcontainers` annotation. +Those attributes can be helpful when: + +* Tests should be skipped instead of failing because Docker is unavailable in the +current environment. Set `disabledWithoutDocker` to `true`. +* Enable parallel container initialization instead of sequential (by default). Set `parallel` to `true`. + +### Step 5: Run the tests! + +That's it! + +Let's look at our complete test class to see how little we had to add to get up and running with Testcontainers: + +```java +@Testcontainers +public class RedisBackedCacheIntTest { + + private RedisBackedCache underTest; + + // container { + @Container + public GenericContainer redis = new GenericContainer(DockerImageName.parse("redis:6-alpine")) + .withExposedPorts(6379); + + // } + + @BeforeEach + public void setUp() { + String address = redis.getHost(); + Integer port = redis.getFirstMappedPort(); + + // Now we have an address and port for Redis, no matter where it is running + underTest = new RedisBackedCache(address, port); + } + + @Test + public void testSimplePutAndGet() { + underTest.put("test", "example"); + + String retrieved = underTest.get("test"); + assertThat(retrieved).isEqualTo("example"); + } +} +``` + +## Spock Quickstart + +It's easy to add Testcontainers to your project - let's walk through a quick example to see how. + +Let's imagine we have a simple program that has a dependency on Redis, and we want to add some tests for it. +In our imaginary program, there is a `RedisBackedCache` class which stores data in Redis. + +You can see an example test that could have been written for it (without using Testcontainers): + +```groovy +class RedisBackedCacheIntTestStep0 extends Specification { + private RedisBackedCache underTest + + void setup() { + // Assume that we have Redis running locally? + underTest = new RedisBackedCache("localhost", 6379) + } + + void testSimplePutAndGet() { + setup: + underTest.put("test", "example") + + when: + String retrieved = underTest.get("test") + + then: + retrieved == "example" + } +} +``` + +Notice that the existing test has a problem - it's relying on a local installation of Redis, which is a red flag for test reliability. +This may work if we were sure that every developer and CI machine had Redis installed, but would fail otherwise. +We might also have problems if we attempted to run tests in parallel, such as state bleeding between tests, or port clashes. + +Let's start from here, and see how to improve the test with Testcontainers: + +### Step 1: Add Testcontainers as a test-scoped dependency + +First, add Testcontainers as a dependency as follows: + +{{< tabs group="lang" >}} +{{< tab name="Gradle" >}} + +```groovy +testImplementation "org.testcontainers:spock:{{latest_version}}" +``` + +{{< /tab >}} +{{< tab name="Maven" >}} + +```xml + + org.testcontainers + spock + {{latest_version}} + test + +``` + +{{< /tab >}} +{{< /tabs >}} + +### Step 2: Get Testcontainers to run a Redis container during our tests + +Annotate the Spock specification class with the Testcontainers extension: + +```groovy +@org.testcontainers.spock.Testcontainers +class RedisBackedCacheIntTest extends Specification { +``` + +And add the following field to the body of our test class: + +```groovy +GenericContainer redis = new GenericContainer<>("redis:6-alpine") + .withExposedPorts(6379) +``` + +This tells Spock to start a Testcontainers `GenericContainer`, configured to use a specific Redis image from Docker Hub, and configured to expose a port. + +If we run our test as-is, then regardless of the actual test outcome, we'll see logs showing us that Testcontainers: + +* was activated before our test method ran +* discovered and quickly tested our local Docker setup +* pulled the image if necessary +* started a new container and waited for it to be ready +* shut down and deleted the container after the test + +### Step 3: Make sure our code can talk to the container + +Before Testcontainers, we might have hardcoded an address like `localhost:6379` into our tests. + +Testcontainers uses *randomized ports* for each container it starts, but makes it easy to obtain the actual port at runtime. +We can do this in our test `setup` method, to set up our component under test: + +```groovy +String address = redis.host +Integer port = redis.firstMappedPort + +// Now we have an address and port for Redis, no matter where it is running +underTest = new RedisBackedCache(address, port) +``` + +> [!TIP] +> +> Notice that we also ask Testcontainers for the container's actual address with `redis.containerIpAddress`, +> rather than hard-coding `localhost`. `localhost` may work in some environments but not others - for example it may +> not work on your current or future CI environment. As such, **avoid hard-coding** the address, and use +> `containerIpAddress` instead. + +### Step 4: Run the tests! + +That's it! + +Let's look at our complete test class to see how little we had to add to get up and running with Testcontainers: + +```groovy +@org.testcontainers.spock.Testcontainers +class RedisBackedCacheIntTest extends Specification { + + private RedisBackedCache underTest + + GenericContainer redis = new GenericContainer<>("redis:6-alpine") + .withExposedPorts(6379) + + void setup() { + String address = redis.host + Integer port = redis.firstMappedPort + + // Now we have an address and port for Redis, no matter where it is running + underTest = new RedisBackedCache(address, port) + } + + void testSimplePutAndGet() { + setup: + underTest.put("test", "example") + + when: + String retrieved = underTest.get("test") + + then: + retrieved == "example" + } +} +``` From b362a21b84b74eb915b2fd82e49af93b43416800 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Fri, 28 Feb 2025 10:20:30 +0100 Subject: [PATCH 3/5] fix: lint --- content/manuals/testcontainers/benefits.md | 2 +- content/manuals/testcontainers/getting-started/go.md | 4 ++-- content/manuals/testcontainers/getting-started/java.md | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/content/manuals/testcontainers/benefits.md b/content/manuals/testcontainers/benefits.md index 41881cd35a3..88c6fc2e023 100644 --- a/content/manuals/testcontainers/benefits.md +++ b/content/manuals/testcontainers/benefits.md @@ -8,7 +8,7 @@ aliases: - /testcontainers/benefits/ --- -### Benefits of using Testcontainers: +### Benefits of using Testcontainers * **On-demand isolated infrastructure provisioning:** You don't need to have a pre-provisioned integration testing infrastructure. diff --git a/content/manuals/testcontainers/getting-started/go.md b/content/manuals/testcontainers/getting-started/go.md index eb479cae52a..f3ba09c864e 100644 --- a/content/manuals/testcontainers/getting-started/go.md +++ b/content/manuals/testcontainers/getting-started/go.md @@ -28,7 +28,7 @@ _Testcontainers for Go_ is tested against those two latest Go releases, therefor _Testcontainers for Go_ uses [go mod](https://blog.golang.org/using-go-modules) and you can get it installed via: -``` +```bash go get github.com/testcontainers/testcontainers-go ``` @@ -93,7 +93,7 @@ automatically handles nil container so is safe to use even in the error case. > [!TIP] > -> Look at [features/garbage_collector](/features/garbage_collector/) to know another +> Look at [features/garbage_collector](https://golang.testcontainers.org/features/garbage_collector/) to know another > way to clean up resources. ## Step 3: Make your code talk to the container diff --git a/content/manuals/testcontainers/getting-started/java.md b/content/manuals/testcontainers/getting-started/java.md index f3587cccd86..af54e763b40 100644 --- a/content/manuals/testcontainers/getting-started/java.md +++ b/content/manuals/testcontainers/getting-started/java.md @@ -117,7 +117,7 @@ underTest = new RedisBackedCache(address, port); > not work on your current or future CI environment. As such, **avoid hard-coding** the address, and use > `getHost()` instead. -### Step 4: Run the tests! +### Step 4: Run the tests That's it! @@ -282,7 +282,7 @@ Those attributes can be helpful when: current environment. Set `disabledWithoutDocker` to `true`. * Enable parallel container initialization instead of sequential (by default). Set `parallel` to `true`. -### Step 5: Run the tests! +### Step 5: Run the tests That's it! @@ -431,7 +431,7 @@ underTest = new RedisBackedCache(address, port) > not work on your current or future CI environment. As such, **avoid hard-coding** the address, and use > `containerIpAddress` instead. -### Step 4: Run the tests! +### Step 4: Run the tests That's it! From 272bb8b223d8dbe1128bb5c6acf70c5547b08e2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Fri, 28 Feb 2025 11:20:42 +0100 Subject: [PATCH 4/5] chore: add Ryuk and JUnit as accepted words --- _vale/config/vocabularies/Docker/accept.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/_vale/config/vocabularies/Docker/accept.txt b/_vale/config/vocabularies/Docker/accept.txt index 647222a9764..4b328de6474 100644 --- a/_vale/config/vocabularies/Docker/accept.txt +++ b/_vale/config/vocabularies/Docker/accept.txt @@ -55,6 +55,7 @@ Intune JFrog Jamf JetBrains +JUnit Kerberos Kitematic Kubeadm @@ -80,6 +81,7 @@ Paketo Postgres PowerShell Python +Ryuk S3 SQLite Slack From 7dc04c284a15a42dee33e91a0012d0f91d742853 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Fri, 28 Feb 2025 11:20:58 +0100 Subject: [PATCH 5/5] fix: lint about using we and more --- content/manuals/testcontainers/benefits.md | 2 +- .../testcontainers/getting-started/_index.md | 4 +- .../testcontainers/getting-started/go.md | 34 ++++--- .../testcontainers/getting-started/java.md | 94 +++++++++---------- 4 files changed, 63 insertions(+), 71 deletions(-) diff --git a/content/manuals/testcontainers/benefits.md b/content/manuals/testcontainers/benefits.md index 88c6fc2e023..c4f4cc9b75e 100644 --- a/content/manuals/testcontainers/benefits.md +++ b/content/manuals/testcontainers/benefits.md @@ -38,4 +38,4 @@ automatically after the test execution is complete by using the Ryuk sidecar con While starting the required containers, Testcontainers attaches a set of labels to the created resources (containers, volumes, networks etc) and Ryuk automatically performs resource clean up by matching those labels. -This works reliably even when the test process exits abnormally (e.g. sending a SIGKILL). +This works reliably even when the test process exits abnormally (for example sending a SIGKILL). diff --git a/content/manuals/testcontainers/getting-started/_index.md b/content/manuals/testcontainers/getting-started/_index.md index 4b13fc92a08..d5aedf2a8d9 100644 --- a/content/manuals/testcontainers/getting-started/_index.md +++ b/content/manuals/testcontainers/getting-started/_index.md @@ -31,5 +31,5 @@ It is possible to configure Testcontainers to work for other Docker setups, such However, these are not actively tested in the main development workflow, so not all Testcontainers features might be available and additional manual configuration might be necessary. -If you have further questions about configuration details for your setup or whether it supports running Testcontainers-based tests, -please contact the Testcontainers team and other users from the Testcontainers community on [Slack](https://slack.testcontainers.org/). +If you have further questions about configuration details for your setup or whether it supports running Testcontainers-based tests, + contact the Testcontainers team and other users from the Testcontainers community on [Slack](https://slack.testcontainers.org/). diff --git a/content/manuals/testcontainers/getting-started/go.md b/content/manuals/testcontainers/getting-started/go.md index f3ba09c864e..a25e7d4ce88 100644 --- a/content/manuals/testcontainers/getting-started/go.md +++ b/content/manuals/testcontainers/getting-started/go.md @@ -22,9 +22,9 @@ From the [Go Release Policy](https://go.dev/doc/devel/release#policy): > Each major Go release is supported until there are two newer major releases. For example, Go 1.5 was supported until the Go 1.7 release, and Go 1.6 was supported until the Go 1.8 release. We fix critical problems, including critical security problems, in supported releases as needed by issuing minor revisions (for example, Go 1.6.1, Go 1.6.2, and so on). -_Testcontainers for Go_ is tested against those two latest Go releases, therefore we recommend using any of them. +_Testcontainers for Go_ is tested against those two latest Go releases, therefore it's recommended to use any of them. -## Step 1:Install _Testcontainers for Go_ +## Step 1: Install _Testcontainers for Go_ _Testcontainers for Go_ uses [go mod](https://blog.golang.org/using-go-modules) and you can get it installed via: @@ -66,28 +66,26 @@ look. * `Image` is the Docker image the container starts from. * `ExposedPorts` lists the ports to be exposed from the container. -* `WaitingFor` is a field you can use to validate when a container is ready. It - is important to get this set because it helps to know when the container is - ready to receive any traffic. In this case, we check for the logs we know come - from Redis, telling us that it is ready to accept requests. +* `WaitingFor` is a field you can use to validate when a container is ready. +It is important to get this set because it helps to know when the container is +ready to receive any traffic. In this case, the project checks for the logs that come from Redis, mentioning that it is ready to accept requests. When you use `ExposedPorts` you have to imagine yourself using `docker run -p -`. When you do so, `dockerd` maps the selected `` from inside the +`. When you do so, `dockerd` maps the selected `` from inside the container to a random one available on your host. -In the previous example, we expose `6379` for `tcp` traffic to the outside. This +In the previous example, the `6379/tcp` port is exposed to the outside. This allows Redis to be reachable from your code that runs outside the container, but -it also makes parallelization possible because if you add `t.Parallel` to your -tests, and each of them starts a Redis container each of them will be exposed on a -different random port. +it also makes parallel execution possible: if you add `t.Parallel` to your +tests, and each of them starts a Redis container, then each of them will be +exposed on a different random port. -`testcontainers.GenericContainer` creates the container. In this example we are -using `Started: true`. It means that the container function will wait for the +`testcontainers.GenericContainer` creates the container. This example uses `Started: true`. It means that the container function will wait for the container to be up and running. If you set the `Start` value to `false` it won't start, leaving to you the decision about when to start it. All the containers must be removed at some point, otherwise they will run until -the host is overloaded. One of the ways we have to clean up is by deferring the +the host is overloaded. You can do the clean up by deferring the terminated function: `defer testcontainers.TerminateContainer(redisC)` which automatically handles nil container so is safe to use even in the error case. @@ -96,11 +94,11 @@ automatically handles nil container so is safe to use even in the error case. > Look at [features/garbage_collector](https://golang.testcontainers.org/features/garbage_collector/) to know another > way to clean up resources. -## Step 3: Make your code talk to the container +## Step 3: Talking to the container This is just an example, but usually Go applications that rely on Redis are using the [redis-go](https://github.com/go-redis/redis) client. This code gets -the endpoint from the container we just started, and it configures the client. +the endpoint from the container that was started before, and it configures the client. ```go endpoint, err := redisC.Endpoint(ctx, "") @@ -115,7 +113,7 @@ client := redis.NewClient(&redis.Options{ _ = client ``` -We expose only one port, so the `Endpoint` does not need a second argument set. +The container exposed only one port, so the `Endpoint` does not need a second argument set. > [!TIP] > @@ -130,4 +128,4 @@ You can run the test via `go test ./...` ## Step 5: Want to go deeper with Redis? -You can find a more elaborated Redis example in our examples section. Please check it out [here](https://golang.testcontainers.org/modules/redis/). +You can find a more elaborated Redis example in the [Redis module docs](https://golang.testcontainers.org/modules/redis/). diff --git a/content/manuals/testcontainers/getting-started/java.md b/content/manuals/testcontainers/getting-started/java.md index af54e763b40..976a5b4f306 100644 --- a/content/manuals/testcontainers/getting-started/java.md +++ b/content/manuals/testcontainers/getting-started/java.md @@ -16,8 +16,8 @@ weight: 20 It's easy to add Testcontainers to your project - let's walk through a quick example to see how. -Let's imagine we have a simple program that has a dependency on Redis, and we want to add some tests for it. -In our imaginary program, there is a `RedisBackedCache` class which stores data in Redis. +Let's imagine you have a simple program that has a dependency on Redis, and you want to add some tests for it. +In your imaginary program, there is a `RedisBackedCache` class which stores data in Redis. You can see an example test that could have been written for it (without using Testcontainers): @@ -43,8 +43,8 @@ public class RedisBackedCacheIntTestStep0 { ``` Notice that the existing test has a problem - it's relying on a local installation of Redis, which is a red flag for test reliability. -This may work if we were sure that every developer and CI machine had Redis installed, but would fail otherwise. -We might also have problems if we attempted to run tests in parallel, such as state bleeding between tests, or port clashes. +This may work if you were sure that every developer and CI machine had Redis installed, but would fail otherwise. +You might also have problems if you attempted to run tests in parallel, such as state bleeding between tests, or port clashes. Let's start from here, and see how to improve the test with Testcontainers: @@ -74,9 +74,9 @@ testImplementation "org.testcontainers:testcontainers:{{latest_version}}" {{< /tab >}} {{< /tabs >}} -### Step 2: Get Testcontainers to run a Redis container during our tests +### Step 2: Run a Redis container in the tests -Simply add the following to the body of our test class: +Simply add the following to the body of your test class: ```java @Rule @@ -85,22 +85,22 @@ public GenericContainer redis = new GenericContainer(DockerImageName.parse("redi ``` The `@Rule` annotation tells JUnit to notify this field about various events in the test lifecycle. -In this case, our rule object is a Testcontainers `GenericContainer`, configured to use a specific Redis image from Docker Hub, and configured to expose a port. +In this case, the rule object is a Testcontainers `GenericContainer`, configured to use a specific Redis image from Docker Hub, and configured to expose a port. -If we run our test as-is, then regardless of the actual test outcome, we'll see logs showing us that Testcontainers: +If you run your test as-is, then regardless of the actual test outcome, you'll see logs showing you that Testcontainers: -* was activated before our test method ran -* discovered and quickly tested our local Docker setup +* was activated before the test method ran +* discovered and quickly tested the local Docker setup * pulled the image if necessary * started a new container and waited for it to be ready * shut down and deleted the container after the test -### Step 3: Make sure our code can talk to the container +### Step 3: Make sure the code can talk to the container -Before Testcontainers, we might have hardcoded an address like `localhost:6379` into our tests. +Before Testcontainers, you might have hard-coded an address like `localhost:6379` into your tests. Testcontainers uses *randomized ports* for each container it starts, but makes it easy to obtain the actual port at runtime. -We can do this in our test `setUp` method, to set up our component under test: +You can do this in your test `setUp` method, to set up your component under test: ```java String address = redis.getHost(); @@ -112,16 +112,14 @@ underTest = new RedisBackedCache(address, port); > [!TIP] > -> Notice that we also ask Testcontainers for the container's actual address with `redis.getHost();`, +> Notice that the previous code also asks Testcontainers for the container's actual address with `redis.getHost();`, > rather than hard-coding `localhost`. `localhost` may work in some environments but not others - for example it may > not work on your current or future CI environment. As such, **avoid hard-coding** the address, and use > `getHost()` instead. ### Step 4: Run the tests -That's it! - -Let's look at our complete test class to see how little we had to add to get up and running with Testcontainers: +Let's look at the complete test class to see how little you had to add to get up and running with Testcontainers: ```java public class RedisBackedCacheIntTest { @@ -158,8 +156,8 @@ public class RedisBackedCacheIntTest { It's easy to add Testcontainers to your project - let's walk through a quick example to see how. -Let's imagine we have a simple program that has a dependency on Redis, and we want to add some tests for it. -In our imaginary program, there is a `RedisBackedCache` class which stores data in Redis. +Let's imagine you have a simple program that has a dependency on Redis, and you want to add some tests for it. +In your imaginary program, there is a `RedisBackedCache` class which stores data in Redis. You can see an example test that could have been written for it (without using Testcontainers): @@ -185,8 +183,8 @@ public class RedisBackedCacheIntTestStep0 { ``` Notice that the existing test has a problem - it's relying on a local installation of Redis, which is a red flag for test reliability. -This may work if we were sure that every developer and CI machine had Redis installed, but would fail otherwise. -We might also have problems if we attempted to run tests in parallel, such as state bleeding between tests, or port clashes. +This may work if you were sure that every developer and CI machine had Redis installed, but would fail otherwise. +You might also have problems if you attempted to run tests in parallel, such as state bleeding between tests, or port clashes. Let's start from here, and see how to improve the test with Testcontainers: @@ -230,9 +228,9 @@ testImplementation "org.testcontainers:junit-jupiter:{{latest_version}}" {{< /tab >}} {{< /tabs >}} -### Step 2: Get Testcontainers to run a Redis container during our tests +### Step 2: Get Testcontainers to run a Redis container during the tests -First, you'll need to annotate the test class with `@Testcontainers`. Furthermore, add the following to the body of our test class: +First, you'll need to annotate the test class with `@Testcontainers`. Furthermore, add the following to the body of the test class: ```java @Container @@ -241,22 +239,22 @@ public GenericContainer redis = new GenericContainer(DockerImageName.parse("redi ``` The `@Container` annotation tells JUnit to notify this field about various events in the test lifecycle. -In this case, our rule object is a Testcontainers `GenericContainer`, configured to use a specific Redis image from Docker Hub, and configured to expose a port. +In this case, the rule object is a Testcontainers `GenericContainer`, configured to use a specific Redis image from Docker Hub, and configured to expose a port. -If we run our test as-is, then regardless of the actual test outcome, we'll see logs showing us that Testcontainers: +If you run your test as-is, then regardless of the actual test outcome, you'll see logs showing you that Testcontainers: -* was activated before our test method ran -* discovered and quickly tested our local Docker setup +* was activated before the test method ran +* discovered and quickly tested the local Docker setup * pulled the image if necessary * started a new container and waited for it to be ready * shut down and deleted the container after the test -### Step 3: Make sure our code can talk to the container +### Step 3: Make sure the code can talk to the container -Before Testcontainers, we might have hardcoded an address like `localhost:6379` into our tests. +Before Testcontainers, you might have hard-coded an address like `localhost:6379` into your tests. Testcontainers uses *randomized ports* for each container it starts, but makes it easy to obtain the actual port at runtime. -We can do this in our test `setUp` method, to set up our component under test: +You can do this in your test `setUp` method, to set up your component under test: ```java String address = redis.getHost(); @@ -268,7 +266,7 @@ underTest = new RedisBackedCache(address, port); > [!TIP] > -> Notice that we also ask Testcontainers for the container's actual address with `redis.getHost();`, +> Notice that the previous code also asks Testcontainers for the container's actual address with `redis.getHost();`, > rather than hard-coding `localhost`. `localhost` may work in some environments but not others - for example it may > not work on your current or future CI environment. As such, **avoid hard-coding** the address, and use > `getHost()` instead. @@ -284,9 +282,7 @@ current environment. Set `disabledWithoutDocker` to `true`. ### Step 5: Run the tests -That's it! - -Let's look at our complete test class to see how little we had to add to get up and running with Testcontainers: +Let's look at the complete test class to see how little you had to add to get up and running with Testcontainers: ```java @Testcontainers @@ -324,8 +320,8 @@ public class RedisBackedCacheIntTest { It's easy to add Testcontainers to your project - let's walk through a quick example to see how. -Let's imagine we have a simple program that has a dependency on Redis, and we want to add some tests for it. -In our imaginary program, there is a `RedisBackedCache` class which stores data in Redis. +Let's imagine you have a simple program that has a dependency on Redis, and you want to add some tests for it. +In your imaginary program, there is a `RedisBackedCache` class which stores data in Redis. You can see an example test that could have been written for it (without using Testcontainers): @@ -352,8 +348,8 @@ class RedisBackedCacheIntTestStep0 extends Specification { ``` Notice that the existing test has a problem - it's relying on a local installation of Redis, which is a red flag for test reliability. -This may work if we were sure that every developer and CI machine had Redis installed, but would fail otherwise. -We might also have problems if we attempted to run tests in parallel, such as state bleeding between tests, or port clashes. +This may work if you were sure that every developer and CI machine had Redis installed, but would fail otherwise. +You might also have problems if you attempted to run tests in parallel, such as state bleeding between tests, or port clashes. Let's start from here, and see how to improve the test with Testcontainers: @@ -383,7 +379,7 @@ testImplementation "org.testcontainers:spock:{{latest_version}}" {{< /tab >}} {{< /tabs >}} -### Step 2: Get Testcontainers to run a Redis container during our tests +### Step 2: Get Testcontainers to run a Redis container during the tests Annotate the Spock specification class with the Testcontainers extension: @@ -392,7 +388,7 @@ Annotate the Spock specification class with the Testcontainers extension: class RedisBackedCacheIntTest extends Specification { ``` -And add the following field to the body of our test class: +And add the following field to the body of the test class: ```groovy GenericContainer redis = new GenericContainer<>("redis:6-alpine") @@ -401,20 +397,20 @@ GenericContainer redis = new GenericContainer<>("redis:6-alpine") This tells Spock to start a Testcontainers `GenericContainer`, configured to use a specific Redis image from Docker Hub, and configured to expose a port. -If we run our test as-is, then regardless of the actual test outcome, we'll see logs showing us that Testcontainers: +If you run the test as-is, then regardless of the actual test outcome, you'll see logs showing you that Testcontainers: -* was activated before our test method ran -* discovered and quickly tested our local Docker setup +* was activated before the test method ran +* discovered and quickly tested the local Docker setup * pulled the image if necessary * started a new container and waited for it to be ready * shut down and deleted the container after the test -### Step 3: Make sure our code can talk to the container +### Step 3: Make sure the code can talk to the container -Before Testcontainers, we might have hardcoded an address like `localhost:6379` into our tests. +Before Testcontainers, you might have hard-coded an address like `localhost:6379` in the tests. Testcontainers uses *randomized ports* for each container it starts, but makes it easy to obtain the actual port at runtime. -We can do this in our test `setup` method, to set up our component under test: +You can do this in your test `setup` method, to set up your component under test: ```groovy String address = redis.host @@ -426,16 +422,14 @@ underTest = new RedisBackedCache(address, port) > [!TIP] > -> Notice that we also ask Testcontainers for the container's actual address with `redis.containerIpAddress`, +> Notice that the previous code also asks Testcontainers for the container's actual address with `redis.containerIpAddress`, > rather than hard-coding `localhost`. `localhost` may work in some environments but not others - for example it may > not work on your current or future CI environment. As such, **avoid hard-coding** the address, and use > `containerIpAddress` instead. ### Step 4: Run the tests -That's it! - -Let's look at our complete test class to see how little we had to add to get up and running with Testcontainers: +Let's look at the complete test class to see how little you had to add to get up and running with Testcontainers: ```groovy @org.testcontainers.spock.Testcontainers