From 92a6764c17c6c78bef95fdf2354ab6c1b7255410 Mon Sep 17 00:00:00 2001 From: Cristian Greco Date: Mon, 24 Mar 2025 12:19:40 +0000 Subject: [PATCH 1/5] Update quickstart docs --- docs/quickstart.md | 142 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 114 insertions(+), 28 deletions(-) diff --git a/docs/quickstart.md b/docs/quickstart.md index d33fe762d..5905e3d2c 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -8,31 +8,32 @@ npm install testcontainers --save-dev ## Example -### JavaScript +As an example, let's spin up and test a Redis container. -Let's spin up and use a Redis container: +First, let's install the dependencies: -```javascript -const redis = require("async-redis"); -const { GenericContainer } = require("testcontainers"); +```bash +npm install redis +``` + +Using your favorite testing library, let's now create a test: + +```ts +import { createClient, RedisClientType } from "redis"; +import { GenericContainer, StartedTestContainer } from "testcontainers"; describe("Redis", () => { - let container; - let redisClient; + let container: StartedTestContainer; + let redisClient: RedisClientType; beforeAll(async () => { - container = await new GenericContainer("redis") - .withExposedPorts(6379) - .start(); - - redisClient = redis.createClient( - container.getMappedPort(6379), - container.getHost(), - ); + container = await new GenericContainer("redis").withExposedPorts(6379).start(); + redisClient = createClient({ url: `redis://${container.getHost()}:${container.getMappedPort(6379)}` }); + await redisClient.connect(); }); afterAll(async () => { - await redisClient.quit(); + await redisClient.disconnect(); await container.stop(); }); @@ -43,19 +44,104 @@ describe("Redis", () => { }); ``` -### TypeScript +Run the test, and after a few seconds, it passes! + +Why did it take a few seconds? Because your container runtime likely had to pull the image first. If you run the test again, it'll likely pass much faster. -Testcontainers is built with TypeScript and offers first-class support for TypeScript users: +It would be nice to see what Testcontainers is doing while the test is running. You can enable all debug logging by setting the `DEBUG` environment variable. For example: -```typescript -import { - TestContainer, - StartedTestContainer, - StoppedTestContainer, - GenericContainer -} from "testcontainers"; +```bash +DEBUG=testcontainers* npm test +``` + +If we run the test again, we'll see a lot of debug output: -const container: TestContainer = new GenericContainer("alpine"); -const startedContainer: StartedTestContainer = await container.start(); -const stoppedContainer: StoppedTestContainer = await startedContainer.stop(); ``` +[DEBUG] Checking container runtime strategy "UnixSocketStrategy"... +[TRACE] Fetching Docker info... +[TRACE] Fetching remote container runtime socket path... +[TRACE] Resolving host... +[TRACE] Fetching Compose info... +[TRACE] Looking up host IPs... +[TRACE] Initialising clients... +[TRACE] Container runtime info: +[DEBUG] Container runtime strategy "UnixSocketStrategy" works +[DEBUG] Checking if image exists "redis:latest"... +[DEBUG] Checked if image exists "redis:latest" +[DEBUG] Pulling image "redis:latest"... +[DEBUG] Executing Docker credential provider "docker-credential-desktop.exe" +[DEBUG] Auth config found for registry "https://index.docker.io/v1/": CredsStore +[redis:latest] {"status":"Pulling from library/redis","id":"latest"} +[redis:latest] {"status":"Pulling fs layer","progressDetail":{},"id":"6e909acdb790"} +... +[redis:latest] {"status":"Status: Downloaded newer image for redis:latest"} +[DEBUG] Pulled image "redis:latest" +[DEBUG] Acquiring lock file "/tmp/testcontainers-node.lock"... +[DEBUG] Acquired lock file "/tmp/testcontainers-node.lock" +[DEBUG] Listing containers... +[DEBUG] Listed containers +[DEBUG] Creating new Reaper for session "4c81d4efc176" with socket path "/var/run/docker.sock"... +[DEBUG] Checking if image exists "testcontainers/ryuk:0.11.0"... +[DEBUG] Checked if image exists "testcontainers/ryuk:0.11.0" +[DEBUG] Image "testcontainers/ryuk:0.11.0" already exists +[DEBUG] Creating container for image "testcontainers/ryuk:0.11.0"... +[DEBUG] [11a9d12ea231] Created container for image "testcontainers/ryuk:0.11.0" +[INFO] [11a9d12ea231] Starting container for image "testcontainers/ryuk:0.11.0"... +[DEBUG] [11a9d12ea231] Starting container... +[DEBUG] [11a9d12ea231] Started container +[INFO] [11a9d12ea231] Started container for image "testcontainers/ryuk:0.11.0" +[DEBUG] [11a9d12ea231] Fetching container logs... +[DEBUG] [11a9d12ea231] Demuxing stream... +[DEBUG] [11a9d12ea231] Demuxed stream +[DEBUG] [11a9d12ea231] Fetched container logs +[DEBUG] [11a9d12ea231] Waiting for container to be ready... +[DEBUG] [11a9d12ea231] Waiting for log message "/.*Started.*/"... +[DEBUG] [11a9d12ea231] Fetching container logs... +[11a9d12ea231] time=2025-03-24T12:10:17.130Z level=INFO msg=starting connection_timeout=1m0s reconnection_timeout=10s request_timeout=10s shutdown_timeout=10m0s remove_retries=10 retry_offset=-1s changes_retry_interval=1s port=8080 verbose=false +[11a9d12ea231] time=2025-03-24T12:10:17.130Z level=INFO msg=Started address=[::]:8080 +[11a9d12ea231] time=2025-03-24T12:10:17.130Z level=INFO msg="client processing started" +[DEBUG] [11a9d12ea231] Demuxing stream... +[DEBUG] [11a9d12ea231] Demuxed stream +[DEBUG] [11a9d12ea231] Fetched container logs +[DEBUG] [11a9d12ea231] Log wait strategy complete +[INFO] [11a9d12ea231] Container is ready +[DEBUG] [11a9d12ea231] Connecting to Reaper (attempt 1) on "localhost:32774"... +[DEBUG] [11a9d12ea231] Connected to Reaper +[DEBUG] Releasing lock file "/tmp/testcontainers-node.lock"... +[DEBUG] Released lock file "/tmp/testcontainers-node.lock" +[DEBUG] Creating container for image "redis:latest"... +[11a9d12ea231] time=2025-03-24T12:10:17.145Z level=INFO msg="client connected" address=172.17.0.1:40446 clients=1 +[11a9d12ea231] time=2025-03-24T12:10:17.145Z level=INFO msg="adding filter" type=label values="[org.testcontainers.session-id=4c81d4efc176]" +[936d82e9964e] Created container for image "redis:latest" +[936d82e9964e] Starting container for image "redis:latest"... +[936d82e9964e] Starting container... +[936d82e9964e] Started container +[936d82e9964e] Started container for image "redis:latest" +[936d82e9964e] Fetching container logs... +[936d82e9964e] Demuxing stream... +[936d82e9964e] Demuxed stream +[936d82e9964e] Fetched container logs +[936d82e9964e] Waiting for container to be ready... +[936d82e9964e] Waiting for host port 32775... +[936d82e9964e] Waiting for internal port 6379... +[936d82e9964e] 1:C 24 Mar 2025 12:10:17.419 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo +[936d82e9964e] 1:C 24 Mar 2025 12:10:17.419 * Redis version=7.4.2, bits=64, commit=00000000, modified=0, pid=1, just started +[936d82e9964e] 1:C 24 Mar 2025 12:10:17.419 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf +[936d82e9964e] 1:M 24 Mar 2025 12:10:17.419 * monotonic clock: POSIX clock_gettime +[936d82e9964e] 1:M 24 Mar 2025 12:10:17.419 * Running mode=standalone, port=6379. +[936d82e9964e] 1:M 24 Mar 2025 12:10:17.420 * Server initialized +[936d82e9964e] 1:M 24 Mar 2025 12:10:17.420 * Ready to accept connections tcp +[DEBUG] [936d82e9964e] Host port 32775 ready +[DEBUG] [936d82e9964e] Host port wait strategy complete +[DEBUG] [936d82e9964e] Internal port 6379 ready +[INFO] [936d82e9964e] Container is ready +[INFO] [936d82e9964e] Stopping container... +[DEBUG] [936d82e9964e] Stopping container... +[936d82e9964e] 1:signal-handler (1742818217) Received SIGTERM scheduling shutdown... +[DEBUG] [936d82e9964e] Stopped container +[DEBUG] [936d82e9964e] Removing container... +[DEBUG] [936d82e9964e] Removed container +[INFO] [936d82e9964e] Stopped container +``` + +These logs are very useful for debugging when a container isn't working as expected. You can see there are logs from the Testcontainers library, as well as logs emitted from all Testcontainers-managed containers. \ No newline at end of file From bcf862e08b5d331719c110cbf34cc5b06b14bbac Mon Sep 17 00:00:00 2001 From: Cristian Greco Date: Mon, 24 Mar 2025 12:32:50 +0000 Subject: [PATCH 2/5] Add more examples to quickstart --- docs/quickstart.md | 68 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/docs/quickstart.md b/docs/quickstart.md index 5905e3d2c..610ecb029 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -27,8 +27,14 @@ describe("Redis", () => { let redisClient: RedisClientType; beforeAll(async () => { - container = await new GenericContainer("redis").withExposedPorts(6379).start(); - redisClient = createClient({ url: `redis://${container.getHost()}:${container.getMappedPort(6379)}` }); + container = await new GenericContainer("redis") + .withExposedPorts(6379) + .start(); + + redisClient = createClient({ + url: `redis://${container.getHost()}:${container.getMappedPort(6379)}` + }); + await redisClient.connect(); }); @@ -46,7 +52,63 @@ describe("Redis", () => { Run the test, and after a few seconds, it passes! -Why did it take a few seconds? Because your container runtime likely had to pull the image first. If you run the test again, it'll likely pass much faster. +Why did it take a few seconds? Because your container runtime likely had to pull the image first. If you run the test again, it'll run faster. + +## Global setup + +If you have a lot of tests that require a Redis container, you might not want to spin up 100s of Redis containers. + +In this case a common pattern is to set the container up globally, and reuse it in your tests. Here's an example using Vitest: + +```ts +// setup.js + +import { createClient, RedisClientType } from "redis"; +import { GenericContainer, StartedTestContainer } from "testcontainers"; + +export async function setup() { + globalThis.redisContainer = await new GenericContainer("redis") + .withExposedPorts(6379) + .start(); + + globalThis.redisClient = createClient({ + url: `redis://${redisContainer.getHost()}:${redisContainer.getMappedPort(6379)}` + }); + + await redisClient.connect(); +} + +export async function teardown() { + await globalThis.redisClient.disconnect(); + await globalThis.redisContainer.stop(); +} +``` + +The Vite configuration: + +```ts +// vite.config.js + +import { defineConfig } from "vite"; + +export default defineConfig({ + test: { + setupFiles: "./setup.js", + } +}); +``` + +And how to reference the container/client in your tests: + +```ts +it("should set and retrieve a value from Redis", async () => { + await globalThis.redisClient.set("key", "test-value"); + const result = await globalThis.redisClient.get("key"); + expect(result).toBe("test-value"); +}); +``` + +## Logging It would be nice to see what Testcontainers is doing while the test is running. You can enable all debug logging by setting the `DEBUG` environment variable. For example: From 38e2e801746f65a1c346d8792cbb88863ab11636 Mon Sep 17 00:00:00 2001 From: Cristian Greco Date: Mon, 24 Mar 2025 13:26:38 +0000 Subject: [PATCH 3/5] Break up quickstart into multiple pages --- docs/quickstart/global-setup.md | 54 +++++++++ docs/quickstart/install.md | 5 + docs/{quickstart.md => quickstart/logging.md} | 114 +----------------- docs/quickstart/usage.md | 47 ++++++++ mkdocs.yml | 6 +- 5 files changed, 113 insertions(+), 113 deletions(-) create mode 100644 docs/quickstart/global-setup.md create mode 100644 docs/quickstart/install.md rename docs/{quickstart.md => quickstart/logging.md} (68%) create mode 100644 docs/quickstart/usage.md diff --git a/docs/quickstart/global-setup.md b/docs/quickstart/global-setup.md new file mode 100644 index 000000000..04d1e11d0 --- /dev/null +++ b/docs/quickstart/global-setup.md @@ -0,0 +1,54 @@ +# Global setup + +If you have a lot of tests that require a Redis container, you might not want to spin up 100s of Redis containers. + +In this case a common pattern is to set the container up globally, and reuse it in your tests. Here's an example using Vitest: + +```ts +// setup.js + +import { createClient, RedisClientType } from "redis"; +import { GenericContainer, StartedTestContainer } from "testcontainers"; + +export async function setup() { + globalThis.redisContainer = await new GenericContainer("redis") + .withExposedPorts(6379) + .start(); + + globalThis.redisClient = createClient({ + url: `redis://${redisContainer.getHost()}:${redisContainer.getMappedPort(6379)}` + }); + + await redisClient.connect(); +} + +export async function teardown() { + await globalThis.redisClient.disconnect(); + await globalThis.redisContainer.stop(); +} +``` + +The Vite configuration: + +```ts +// vite.config.js + +import { defineConfig } from "vite"; + +export default defineConfig({ + test: { + setupFiles: "./setup.js", + } +}); +``` + +And to reference the container/client in your tests: + +```ts +it("should set and retrieve a value from Redis", async () => { + await globalThis.redisClient.set("key", "test-value"); + const result = await globalThis.redisClient.get("key"); + + expect(result).toBe("test-value"); +}); +``` diff --git a/docs/quickstart/install.md b/docs/quickstart/install.md new file mode 100644 index 000000000..1aef43bd5 --- /dev/null +++ b/docs/quickstart/install.md @@ -0,0 +1,5 @@ +# Install + +```bash +npm install testcontainers --save-dev +``` diff --git a/docs/quickstart.md b/docs/quickstart/logging.md similarity index 68% rename from docs/quickstart.md rename to docs/quickstart/logging.md index 610ecb029..796b98d9b 100644 --- a/docs/quickstart.md +++ b/docs/quickstart/logging.md @@ -1,114 +1,4 @@ -# Quickstart - -## Install - -```bash -npm install testcontainers --save-dev -``` - -## Example - -As an example, let's spin up and test a Redis container. - -First, let's install the dependencies: - -```bash -npm install redis -``` - -Using your favorite testing library, let's now create a test: - -```ts -import { createClient, RedisClientType } from "redis"; -import { GenericContainer, StartedTestContainer } from "testcontainers"; - -describe("Redis", () => { - let container: StartedTestContainer; - let redisClient: RedisClientType; - - beforeAll(async () => { - container = await new GenericContainer("redis") - .withExposedPorts(6379) - .start(); - - redisClient = createClient({ - url: `redis://${container.getHost()}:${container.getMappedPort(6379)}` - }); - - await redisClient.connect(); - }); - - afterAll(async () => { - await redisClient.disconnect(); - await container.stop(); - }); - - it("works", async () => { - await redisClient.set("key", "val"); - expect(await redisClient.get("key")).toBe("val"); - }); -}); -``` - -Run the test, and after a few seconds, it passes! - -Why did it take a few seconds? Because your container runtime likely had to pull the image first. If you run the test again, it'll run faster. - -## Global setup - -If you have a lot of tests that require a Redis container, you might not want to spin up 100s of Redis containers. - -In this case a common pattern is to set the container up globally, and reuse it in your tests. Here's an example using Vitest: - -```ts -// setup.js - -import { createClient, RedisClientType } from "redis"; -import { GenericContainer, StartedTestContainer } from "testcontainers"; - -export async function setup() { - globalThis.redisContainer = await new GenericContainer("redis") - .withExposedPorts(6379) - .start(); - - globalThis.redisClient = createClient({ - url: `redis://${redisContainer.getHost()}:${redisContainer.getMappedPort(6379)}` - }); - - await redisClient.connect(); -} - -export async function teardown() { - await globalThis.redisClient.disconnect(); - await globalThis.redisContainer.stop(); -} -``` - -The Vite configuration: - -```ts -// vite.config.js - -import { defineConfig } from "vite"; - -export default defineConfig({ - test: { - setupFiles: "./setup.js", - } -}); -``` - -And how to reference the container/client in your tests: - -```ts -it("should set and retrieve a value from Redis", async () => { - await globalThis.redisClient.set("key", "test-value"); - const result = await globalThis.redisClient.get("key"); - expect(result).toBe("test-value"); -}); -``` - -## Logging +# Logging It would be nice to see what Testcontainers is doing while the test is running. You can enable all debug logging by setting the `DEBUG` environment variable. For example: @@ -206,4 +96,4 @@ If we run the test again, we'll see a lot of debug output: [INFO] [936d82e9964e] Stopped container ``` -These logs are very useful for debugging when a container isn't working as expected. You can see there are logs from the Testcontainers library, as well as logs emitted from all Testcontainers-managed containers. \ No newline at end of file +These logs are very useful for debugging when a container isn't working as expected. You can see there are logs from the Testcontainers library, as well as logs emitted from all Testcontainers-managed containers. diff --git a/docs/quickstart/usage.md b/docs/quickstart/usage.md new file mode 100644 index 000000000..0610b8955 --- /dev/null +++ b/docs/quickstart/usage.md @@ -0,0 +1,47 @@ +# Usage + +As an example, let's spin up and test a Redis container. + +First, let's install the dependencies: + +```bash +npm install redis +``` + +Using your favorite testing library, let's now create a test: + +```ts +import { createClient, RedisClientType } from "redis"; +import { GenericContainer, StartedTestContainer } from "testcontainers"; + +describe("Redis", () => { + let container: StartedTestContainer; + let redisClient: RedisClientType; + + beforeAll(async () => { + container = await new GenericContainer("redis") + .withExposedPorts(6379) + .start(); + + redisClient = createClient({ + url: `redis://${container.getHost()}:${container.getMappedPort(6379)}` + }); + + await redisClient.connect(); + }); + + afterAll(async () => { + await redisClient.disconnect(); + await container.stop(); + }); + + it("works", async () => { + await redisClient.set("key", "val"); + expect(await redisClient.get("key")).toBe("val"); + }); +}); +``` + +Run the test, and after a few seconds, it passes! + +Why did it take a few seconds? Because your container runtime likely had to pull the image first. If you run the test again, it'll run faster. diff --git a/mkdocs.yml b/mkdocs.yml index 8fcfa52df..e749ca1ff 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -33,7 +33,11 @@ markdown_extensions: nav: - Home: index.md - - Quickstart: quickstart.md + - Quickstart: + - Install: quickstart/install.md + - Usage: quickstart/usage.md + - Logging: quickstart/logging.md + - Global setup: quickstart/global-setup.md - Supported container runtimes: supported-container-runtimes.md - Features: - Containers: features/containers.md From 3d3cf320c73cb5126d620f2a546c10f2a3358a43 Mon Sep 17 00:00:00 2001 From: Cristian Greco Date: Mon, 24 Mar 2025 13:37:21 +0000 Subject: [PATCH 4/5] Cleanup --- docs/quickstart/global-setup.md | 4 +--- docs/quickstart/install.md | 18 ++++++++++++++++ docs/quickstart/logging.md | 38 ++++++++++++++++++++++++++++++++- mkdocs.yml | 2 +- 4 files changed, 57 insertions(+), 5 deletions(-) diff --git a/docs/quickstart/global-setup.md b/docs/quickstart/global-setup.md index 04d1e11d0..ed21a093f 100644 --- a/docs/quickstart/global-setup.md +++ b/docs/quickstart/global-setup.md @@ -1,6 +1,6 @@ # Global setup -If you have a lot of tests that require a Redis container, you might not want to spin up 100s of Redis containers. +If you have a lot of tests that require the same container, you might not want to spin up one per test. In this case a common pattern is to set the container up globally, and reuse it in your tests. Here's an example using Vitest: @@ -28,8 +28,6 @@ export async function teardown() { } ``` -The Vite configuration: - ```ts // vite.config.js diff --git a/docs/quickstart/install.md b/docs/quickstart/install.md index 1aef43bd5..7283e63e5 100644 --- a/docs/quickstart/install.md +++ b/docs/quickstart/install.md @@ -1,5 +1,23 @@ # Install +Install the Testcontainers dependency. + +It is up to you whether or not to install it as a dev dependency. + +## NPM + ```bash npm install testcontainers --save-dev ``` + +## Yarn + +```bash +yarn add testcontainers --dev +``` + +## PNPM + +```bash +pnpm add testcontainers --save-dev +``` diff --git a/docs/quickstart/logging.md b/docs/quickstart/logging.md index 796b98d9b..dc51718c8 100644 --- a/docs/quickstart/logging.md +++ b/docs/quickstart/logging.md @@ -1,6 +1,6 @@ # Logging -It would be nice to see what Testcontainers is doing while the test is running. You can enable all debug logging by setting the `DEBUG` environment variable. For example: +It would be nice to see what Testcontainers is doing while the test is running. You can enable all logs by setting the `DEBUG` environment variable. For example: ```bash DEBUG=testcontainers* npm test @@ -17,6 +17,42 @@ If we run the test again, we'll see a lot of debug output: [TRACE] Looking up host IPs... [TRACE] Initialising clients... [TRACE] Container runtime info: +{ + "node": { + "version": "v22.14.0", + "architecture": "x64", + "platform": "linux" + }, + "containerRuntime": { + "host": "localhost", + "hostIps": [ + { + "address": "127.0.0.1", + "family": 4 + } + ], + "remoteSocketPath": "/var/run/docker.sock", + "indexServerAddress": "https://index.docker.io/v1/", + "serverVersion": "28.0.1", + "operatingSystem": "Docker Desktop", + "operatingSystemType": "linux", + "architecture": "x86_64", + "cpus": 32, + "memory": 33524871168, + "runtimes": [ + "io.containerd.runc.v2", + "nvidia", + "runc" + ], + "labels": [ + "com.docker.desktop.address=unix:///var/run/docker-cli.sock" + ] + }, + "compose": { + "version": "2.33.1-desktop.1", + "compatability": "v2" + } +} [DEBUG] Container runtime strategy "UnixSocketStrategy" works [DEBUG] Checking if image exists "redis:latest"... [DEBUG] Checked if image exists "redis:latest" diff --git a/mkdocs.yml b/mkdocs.yml index e749ca1ff..be01dd2ca 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -36,8 +36,8 @@ nav: - Quickstart: - Install: quickstart/install.md - Usage: quickstart/usage.md - - Logging: quickstart/logging.md - Global setup: quickstart/global-setup.md + - Logging: quickstart/logging.md - Supported container runtimes: supported-container-runtimes.md - Features: - Containers: features/containers.md From ad64477e165264ac927b437e654a9ca6f75be9f5 Mon Sep 17 00:00:00 2001 From: Cristian Greco Date: Mon, 24 Mar 2025 13:43:00 +0000 Subject: [PATCH 5/5] Update quickstart --- docs/quickstart/install.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/quickstart/install.md b/docs/quickstart/install.md index 7283e63e5..9989edc6e 100644 --- a/docs/quickstart/install.md +++ b/docs/quickstart/install.md @@ -2,8 +2,6 @@ Install the Testcontainers dependency. -It is up to you whether or not to install it as a dev dependency. - ## NPM ```bash