mirror of
https://github.com/docker/docs.git
synced 2026-06-19 07:35:16 +00:00
docs(guides): add two testcontainers intro guides (go and python) (#24450)
## Description Migrate the first two Testcontainers getting-started guides from [testcontainers.com/guides](https://testcontainers.com/guides/) into the Docker docs site: - **Getting started with Testcontainers for Go** — multi-page guide with 4 chapters (create project, write tests, test suites, run tests). Code updated to testcontainers-go v0.41.0 API (`postgres.Run()`, `CleanupContainer`, `BasicWaitStrategies()`). - **Getting started with Testcontainers for Python** — multi-page guide with 3 chapters (create project, write tests, run tests). Code updated to testcontainers-python 4.14.2 (fixed `get_exposed_port()` returning `int`). Each guide appears as its own entry in the `/guides/` listing with proper language and tag filters (`testing-with-docker`). Chapters render with stepper navigation in the sidebar. Also adds: - A `testing-with-docker` tag to `data/tags.yaml` - A Claude skill (`.claude/skills/testcontainers-guides-migrator/SKILL.md`) that documents the repeatable migration process for the remaining 19 guides - Links from `content/manuals/testcontainers.md` to the new guides - Vale vocabulary entries for `pgx`, `Micronaut`, `psycopg`, `pytest` All guide code was compiled and tests verified passing in containers with Docker socket mounted. ## Related issues or tickets No related issues found. ## Reviews - [ ] Technical review - [ ] Editorial review - [ ] Product review
This commit is contained in:
committed by
GitHub
parent
09842d4594
commit
36d384d2da
@@ -0,0 +1,315 @@
|
||||
---
|
||||
name: testcontainers-guide-migrator
|
||||
description: >
|
||||
Migrate a Testcontainers guide from testcontainers.com into the Docker docs site (docs.docker.com).
|
||||
Converts AsciiDoc to Hugo Markdown, updates code to the latest Testcontainers API, splits into
|
||||
chapters with stepper navigation, verifies code compiles and tests pass, and validates against
|
||||
Docker docs style rules. Use when asked to migrate a testcontainers guide, add a TC guide, or
|
||||
port content from testcontainers.com to Docker docs.
|
||||
---
|
||||
|
||||
# Migrate a Testcontainers Guide
|
||||
|
||||
You are migrating guides from https://testcontainers.com/guides/ into the Docker docs Hugo site.
|
||||
Each guide lives in its own GitHub repo under `testcontainers/tc-guide-*`, written in AsciiDoc.
|
||||
The source repos are listed in the testcontainers-site build.sh:
|
||||
https://github.com/testcontainers/testcontainers-site/blob/main/build.sh#L23-L45
|
||||
|
||||
## Inputs
|
||||
|
||||
The user provides one or more guides to migrate. Resolve these from the inventory below:
|
||||
|
||||
- **REPO_NAME**: GitHub repo (e.g. `tc-guide-getting-started-with-testcontainers-for-java`)
|
||||
- **SLUG**: guide slug inside `guide/` dir (e.g. `getting-started-with-testcontainers-for-java`)
|
||||
- **LANG**: language identifier (go, java, dotnet, nodejs, python)
|
||||
- **GUIDE_ID**: short kebab-case name (e.g. `getting-started`)
|
||||
|
||||
## Guide inventory
|
||||
|
||||
These are the 21 guides from testcontainers.com/guides/ and their source repos:
|
||||
|
||||
| # | Title | Repo | Lang | GUIDE_ID |
|
||||
|---|-------|------|------|----------|
|
||||
| 1 | Introduction to Testcontainers | tc-guide-introducing-testcontainers | (none) | introducing |
|
||||
| 2 | Getting started for Java | tc-guide-getting-started-with-testcontainers-for-java | java | getting-started |
|
||||
| 3 | Testing Spring Boot REST API | tc-guide-testing-spring-boot-rest-api | java | spring-boot-rest-api |
|
||||
| 4 | Testcontainers lifecycle (JUnit 5) | tc-guide-testcontainers-lifecycle | java | lifecycle |
|
||||
| 5 | Configuration of services in container | tc-guide-configuration-of-services-running-in-container | java | service-configuration |
|
||||
| 6 | Replace H2 with real database | tc-guide-replace-h2-with-real-database-for-testing | java | replace-h2 |
|
||||
| 7 | Testing ASP.NET Core web app | tc-guide-testing-aspnet-core | dotnet | aspnet-core |
|
||||
| 8 | Testing Spring Boot Kafka Listener | tc-guide-testing-spring-boot-kafka-listener | java | spring-boot-kafka |
|
||||
| 9 | REST API integrations with MockServer | tc-guide-testing-rest-api-integrations-using-mockserver | java | mockserver |
|
||||
| 10 | Getting started for .NET | tc-guide-getting-started-with-testcontainers-for-dotnet | dotnet | getting-started |
|
||||
| 11 | AWS integrations with LocalStack | tc-guide-testing-aws-service-integrations-using-localstack | java | aws-localstack |
|
||||
| 12 | Testcontainers in Quarkus apps | tc-guide-testcontainers-in-quarkus-applications | java | quarkus |
|
||||
| 13 | Getting started for Go | tc-guide-getting-started-with-testcontainers-for-go | go | getting-started |
|
||||
| 14 | jOOQ and Flyway with Testcontainers | tc-guide-working-with-jooq-flyway-using-testcontainers | java | jooq-flyway |
|
||||
| 15 | Getting started for Node.js | tc-guide-getting-started-with-testcontainers-for-nodejs | nodejs | getting-started |
|
||||
| 16 | REST API integrations with WireMock | tc-guide-testing-rest-api-integrations-using-wiremock | java | wiremock |
|
||||
| 17 | Local dev with Testcontainers Desktop | tc-guide-simple-local-development-with-testcontainers-desktop | java | local-dev-desktop |
|
||||
| 18 | Micronaut REST API with WireMock | tc-guide-testing-rest-api-integrations-in-micronaut-apps-using-wiremock | java | micronaut-wiremock |
|
||||
| 19 | Micronaut Kafka Listener | tc-guide-testing-micronaut-kafka-listener | java | micronaut-kafka |
|
||||
| 20 | Getting started for Python | tc-guide-getting-started-with-testcontainers-for-python | python | getting-started |
|
||||
| 21 | Keycloak with Spring Boot | tc-guide-securing-spring-boot-microservice-using-keycloak-and-testcontainers | java | keycloak-spring-boot |
|
||||
|
||||
Already migrated: **#13 (Go getting-started)**, **#20 (Python getting-started)**
|
||||
|
||||
## Step 0: Pre-flight
|
||||
|
||||
1. Confirm `testing-with-docker` tag exists in `data/tags.yaml`. If not, add:
|
||||
```yaml
|
||||
testing-with-docker:
|
||||
title: Testing with Docker
|
||||
```
|
||||
2. Check if new terms need adding to `_vale/config/vocabularies/Docker/accept.txt`.
|
||||
3. Read `STYLE.md` and `COMPONENTS.md` to refresh on Docker docs conventions.
|
||||
|
||||
## Step 1: Clone the guide repo
|
||||
|
||||
Clone the guide repo to a temporary directory. This gives you all source files locally — no HTTP calls needed.
|
||||
|
||||
```bash
|
||||
git clone --depth 1 https://github.com/testcontainers/{REPO_NAME}.git <tmpdir>/{REPO_NAME}
|
||||
```
|
||||
|
||||
Where `<tmpdir>` is a temporary directory on your system (e.g. the output of `mktemp -d`).
|
||||
|
||||
The repo structure is:
|
||||
- `<tmpdir>/{REPO_NAME}/guide/{SLUG}/index.adoc` — the AsciiDoc guide source
|
||||
- `<tmpdir>/{REPO_NAME}/src/` — application source code (referenced by `include::` directives)
|
||||
- `<tmpdir>/{REPO_NAME}/testdata/` — test data files (SQL scripts, configs, etc.)
|
||||
- `<tmpdir>/{REPO_NAME}/pom.xml` or `go.mod` — build config
|
||||
|
||||
1. Read `guide/{SLUG}/index.adoc` to get the guide content.
|
||||
2. Find all `include::{codebase}/path/to/file[]` directives. The `{codebase}` attribute points to a remote URL, but since you have the repo cloned, read the files directly from disk instead (e.g. `include::{codebase}/src/main/java/Foo.java[]` → read `<tmpdir>/{REPO_NAME}/src/main/java/Foo.java`).
|
||||
3. If includes have `[lines="X..Y"]`, extract only those lines from the local file.
|
||||
4. Note the `[source,lang]` block preceding each include — that determines the code fence language.
|
||||
|
||||
This cloned repo also serves as the base for Step 6 (code verification) — you can run the tests directly in it to confirm they pass before updating the code to the latest API.
|
||||
|
||||
## Step 2: Convert AsciiDoc to Markdown
|
||||
|
||||
| AsciiDoc | Markdown |
|
||||
|---|---|
|
||||
| `== Heading` | `## Heading` |
|
||||
| `=== Heading` | `### Heading` |
|
||||
| `*bold*` (AsciiDoc bold) | `**bold**` |
|
||||
| `https://url[Link text]` | `[Link text](url)` |
|
||||
| `[source,lang]\n----\ncode\n----` | `` ```lang\ncode\n``` `` |
|
||||
| `[source,shell]` with `$` prompts | `` ```console `` |
|
||||
| `[NOTE]\ntext` or `====\n[NOTE]\n...\n====` | `> [!NOTE]\n> text` |
|
||||
| `[TIP]\ntext` | `> [!TIP]\n> text` |
|
||||
| `:toc:`, `:toclevels:`, `:codebase:` | Remove entirely |
|
||||
| `include::{codebase}/path[]` | Replace with fetched code in a code fence |
|
||||
| YAML front matter (date, draft, repo) | Remove; transform to Docker docs format |
|
||||
|
||||
## Step 3: Apply Docker docs style rules
|
||||
|
||||
These are mandatory (from STYLE.md and AGENTS.md):
|
||||
|
||||
- **No "we"**: "We are going to create" → "Create" or "Start by creating"
|
||||
- **No "let us" / "let's"**: → imperative voice or "You can..."
|
||||
- **No hedge words**: remove "simply", "easily", "just", "seamlessly"
|
||||
- **No meta-commentary**: remove "it's worth noting", "it's important to understand"
|
||||
- **No "allows you to" / "enables you to"**: → "lets you" or rephrase
|
||||
- **No "click"**: → "select"
|
||||
- **No bold for emphasis or product names**: only bold UI elements
|
||||
- **No time-relative language**: remove "currently", "new", "recently", "now"
|
||||
- **No exclamations**: remove "Voila!!!" etc.
|
||||
- Use `console` language hint for interactive shell blocks with `$` prompts
|
||||
- Use contractions: "it's", "you're", "don't"
|
||||
|
||||
## Step 4: Update code to latest Testcontainers API
|
||||
|
||||
Research the latest API version for the target language before writing code.
|
||||
|
||||
**Best practices reference**: The Testcontainers team maintains Claude skills with up-to-date API patterns and best practices for each language at https://github.com/testcontainers/claude-skills/ — check the relevant language skill (testcontainers-go, testcontainers-node, testcontainers-dotnet) for current API signatures, cleanup patterns, wait strategies, and anti-patterns to avoid.
|
||||
|
||||
For each language, check the cloned repo's existing code, then update to the latest API. Key patterns per language:
|
||||
|
||||
**Go** (testcontainers-go v0.41.0):
|
||||
- `postgres.RunContainer(ctx, opts...)` → `postgres.Run(ctx, "image", opts...)`
|
||||
- `testcontainers.WithImage(...)` → image is now the 2nd positional param to `Run()`
|
||||
- Manual `WithWaitStrategy(wait.ForLog(...))` → `postgres.BasicWaitStrategies()`
|
||||
- `t.Cleanup(func() { ctr.Terminate(ctx) })` → `testcontainers.CleanupContainer(t, ctr)`
|
||||
- `if err != nil { log.Fatal(err) }` → `require.NoError(t, err)` (use testify require/assert)
|
||||
- Helper functions should accept `t *testing.T` as first param, call `t.Helper()`
|
||||
- No `TearDownSuite()` needed if `CleanupContainer` is registered in the helper
|
||||
- Go version prerequisite: 1.25+
|
||||
|
||||
**Java** (testcontainers-java):
|
||||
- Check the latest BOM version at https://java.testcontainers.org/
|
||||
- Use `@Testcontainers` and `@Container` annotations for JUnit 5 lifecycle
|
||||
- Prefer module-specific containers (e.g. `PostgreSQLContainer`) over `GenericContainer`
|
||||
- Use `@DynamicPropertySource` for Spring Boot integration
|
||||
|
||||
**.NET** (testcontainers-dotnet):
|
||||
- Check the latest NuGet package version
|
||||
- Use `IAsyncLifetime` for container lifecycle in xUnit
|
||||
- Use builder pattern: `new PostgreSqlBuilder().Build()`
|
||||
|
||||
**Node.js** (testcontainers-node):
|
||||
- Check the latest npm version
|
||||
- Use module-specific packages (e.g. `@testcontainers/postgresql`)
|
||||
- Use `GenericContainer` for services without a dedicated module
|
||||
|
||||
**Python** (testcontainers-python):
|
||||
- Check the latest PyPI version
|
||||
- Use context managers (`with PostgresContainer() as postgres:`)
|
||||
- Use module-specific containers when available
|
||||
|
||||
For all languages: consult the corresponding Testcontainers skill at https://github.com/testcontainers/claude-skills/ for current best practices and anti-patterns.
|
||||
|
||||
## Step 5: Create guide directory structure
|
||||
|
||||
Directory: `content/guides/testcontainers-{LANG}-{GUIDE_ID}/`
|
||||
|
||||
Each guide is its own top-level entry under `/guides/`. Do NOT nest guides inside a shared parent section — otherwise they won't appear individually in the tag/language filters on the guides listing page.
|
||||
|
||||
### _index.md (landing page)
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: {Full guide title}
|
||||
linkTitle: {Short title for guides listing}
|
||||
description: {One-line description}
|
||||
keywords: testcontainers, {lang}, testing, {technologies used}
|
||||
summary: |
|
||||
{2-3 line summary for the guides listing card}
|
||||
toc_min: 1
|
||||
toc_max: 2
|
||||
tags: [testing-with-docker]
|
||||
languages: [{lang}]
|
||||
params:
|
||||
time: {estimated} minutes
|
||||
---
|
||||
|
||||
<!-- Source: https://github.com/testcontainers/{REPO_NAME} -->
|
||||
```
|
||||
|
||||
Content: what you'll learn (bulleted list), prerequisites, and a NOTE linking to `https://testcontainers.com/getting-started/` for newcomers.
|
||||
|
||||
### Sub-pages (chapters)
|
||||
|
||||
Split the guide into logical chapters. Each sub-page:
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: {Chapter title}
|
||||
linkTitle: {Short title for stepper}
|
||||
description: {One-line description}
|
||||
weight: {10, 20, 30, ...}
|
||||
---
|
||||
```
|
||||
|
||||
**No `tags`, `languages`, or `params` on sub-pages** — only on `_index.md`.
|
||||
|
||||
Typical chapter breakdown:
|
||||
| Weight | File | Content |
|
||||
|--------|------|---------|
|
||||
| 10 | `create-project.md` | Project setup, dependencies, business logic |
|
||||
| 20 | `write-tests.md` | First test using testcontainers |
|
||||
| 30 | `test-suites.md` | Reusing containers, test helpers, suites |
|
||||
| 40 | `run-tests.md` | Running tests, summary, further reading |
|
||||
|
||||
Adapt the split to the guide's content — some guides may need fewer or more chapters.
|
||||
|
||||
## Step 6: Verify code compiles and tests pass
|
||||
|
||||
This is CRITICAL. The code in the guide MUST compile and all tests MUST pass. Do not skip this step.
|
||||
|
||||
### 6a: Use the cloned repo as the verification project
|
||||
|
||||
The repo you cloned in Step 1 (`<tmpdir>/{REPO_NAME}`) already contains a working project with all source files, build config, and tests. Use it as the starting point:
|
||||
|
||||
```bash
|
||||
cd <tmpdir>/{REPO_NAME}
|
||||
```
|
||||
|
||||
First, verify the **original** code compiles and tests pass before you change anything. This confirms a good baseline.
|
||||
|
||||
### 6b: Update the code in the cloned repo
|
||||
|
||||
After confirming the original works, apply the API updates (from Step 4) directly in the cloned repo's source files. This is the same code you're putting in the guide — keep them in sync.
|
||||
|
||||
### 6c: Update dependencies and compile
|
||||
|
||||
Run compilation inside a container for reproducibility — no need to install the language toolchain on the host. Use the appropriate language Docker image, mounting the cloned repo:
|
||||
|
||||
```bash
|
||||
docker run --rm -v "<tmpdir>/{REPO_NAME}":/app -w /app <language-image> sh -c "<compile command>"
|
||||
```
|
||||
|
||||
Pick the right image for the language (e.g. `golang:1.25-alpine`, `maven:3-eclipse-temurin-21`, `gradle:jdk21`, `mcr.microsoft.com/dotnet/sdk:9.0`, `node:22-alpine`, `python:3.13-alpine`). Update dependencies to the latest Testcontainers version and compile.
|
||||
|
||||
If compilation fails, fix the code and update the guide markdown to match.
|
||||
|
||||
### 6d: Run tests in a container with Docker socket mounted
|
||||
|
||||
Run tests in the same kind of container, but **mount the Docker socket** so Testcontainers can create sibling containers:
|
||||
|
||||
```bash
|
||||
docker run --rm \
|
||||
-v "<tmpdir>/{REPO_NAME}":/app \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-w /app <language-image> \
|
||||
sh -c "<test command>"
|
||||
```
|
||||
|
||||
The key is `-v /var/run/docker.sock:/var/run/docker.sock` — this lets Testcontainers inside the container talk to the host's Docker daemon and create sibling containers.
|
||||
|
||||
### 6e: Fix until green
|
||||
|
||||
If any test fails, debug and fix the code in both the temporary project AND the guide markdown. Re-run until all tests pass. Do not proceed until verified.
|
||||
|
||||
## Step 7: Update cross-references
|
||||
|
||||
1. **`content/manuals/testcontainers.md`**: Add a bullet under the `## Guides` section:
|
||||
```markdown
|
||||
- [Guide title](/guides/testcontainers-{LANG}-{GUIDE_ID}/)
|
||||
```
|
||||
2. **Do NOT update** `content/guides/testcontainers-cloud/_index.md` — keep its external links.
|
||||
3. Link to `https://testcontainers.com/getting-started/` for the Testcontainers overview.
|
||||
4. Use internal paths for already-migrated guides; keep `testcontainers.com` links for unmigrated ones.
|
||||
|
||||
## Step 8: Validate
|
||||
|
||||
1. `npx prettier --write content/guides/testcontainers-{LANG}-{GUIDE_ID}/`
|
||||
2. `npx prettier --write content/manuals/testcontainers.md`
|
||||
3. `docker buildx bake lint` — must pass
|
||||
4. `docker buildx bake vale` — check `tmp/vale.out` for errors in new files
|
||||
- Spelling errors for tech terms: add to `_vale/config/vocabularies/Docker/accept.txt`
|
||||
5. Verify in local dev server (`HUGO_PORT=1314 docker compose watch`):
|
||||
- Guide appears when filtering by its language
|
||||
- Guide appears when filtering by `Testing with Docker` tag
|
||||
- Stepper navigation works across chapters
|
||||
- All links resolve (no 404s)
|
||||
6. Verify all external URLs return 200:
|
||||
```bash
|
||||
curl -s -o /dev/null -w "%{http_code}" -L "{url}"
|
||||
```
|
||||
|
||||
## Step 9: Commit
|
||||
|
||||
One commit per guide. Message format:
|
||||
```
|
||||
feat(guides): add testcontainers {lang} {guide-id} guide
|
||||
|
||||
Migrated from https://github.com/testcontainers/{REPO_NAME}
|
||||
Updated to testcontainers-{lang} v{version} API.
|
||||
```
|
||||
|
||||
## Special cases
|
||||
|
||||
- **introducing-testcontainers**: Language-agnostic, conceptual. May overlap with `content/manuals/testcontainers.md`. Review for deduplication before migrating.
|
||||
- **local-dev-testcontainers-desktop**: About Testcontainers Desktop (now part of Docker Desktop). May need significant rewriting rather than mechanical migration.
|
||||
- **Java guides**: Many share the same language. Each still gets its own `testcontainers-java-{GUIDE_ID}` directory.
|
||||
|
||||
## Reference: completed migration (Go getting-started)
|
||||
|
||||
Use `content/guides/testcontainers-go-getting-started/` as the reference implementation:
|
||||
- `_index.md` — landing page with frontmatter, prerequisites, learning objectives
|
||||
- `create-project.md` (weight: 10) — project setup and business logic
|
||||
- `write-tests.md` (weight: 20) — first test with testcontainers-go
|
||||
- `test-suites.md` (weight: 30) — container reuse with testify suites
|
||||
- `run-tests.md` (weight: 40) — running tests, summary, further reading
|
||||
@@ -167,11 +167,14 @@ Paketo
|
||||
PAT
|
||||
perl
|
||||
pgAdmin
|
||||
pgx
|
||||
PKG
|
||||
plaintext
|
||||
plist
|
||||
pluggable
|
||||
Postgres
|
||||
psycopg
|
||||
pytest
|
||||
PowerShell
|
||||
Python
|
||||
Qualcomm
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
title: Getting started with Testcontainers for Go
|
||||
linkTitle: Testcontainers for Go
|
||||
description: Learn how to use Testcontainers for Go to test database interactions with a real PostgreSQL instance.
|
||||
keywords: testcontainers, go, golang, testing, postgresql, integration testing
|
||||
summary: |
|
||||
Learn how to create a Go application and test database interactions
|
||||
using Testcontainers for Go with a real PostgreSQL instance.
|
||||
toc_min: 1
|
||||
toc_max: 2
|
||||
tags: [testing-with-docker]
|
||||
languages: [go]
|
||||
params:
|
||||
time: 20 minutes
|
||||
---
|
||||
|
||||
<!-- Source: https://github.com/testcontainers/tc-guide-getting-started-with-testcontainers-for-go -->
|
||||
|
||||
In this guide, you will learn how to:
|
||||
|
||||
- Create a Go application with modules support
|
||||
- Implement a Repository to manage customer data in a PostgreSQL database using the pgx driver
|
||||
- Write integration tests using testcontainers-go
|
||||
- Reuse containers across multiple tests using test suites
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Go 1.25+
|
||||
- Your preferred IDE (VS Code, GoLand)
|
||||
- A Docker environment supported by Testcontainers. For details, see
|
||||
the [testcontainers-go system requirements](https://golang.testcontainers.org/system_requirements/).
|
||||
|
||||
> [!NOTE]
|
||||
> If you're new to Testcontainers, visit the
|
||||
> [Testcontainers overview](https://testcontainers.com/getting-started/) to learn more about
|
||||
> Testcontainers and the benefits of using it.
|
||||
@@ -0,0 +1,103 @@
|
||||
---
|
||||
title: Create the Go project
|
||||
linkTitle: Create the project
|
||||
description: Set up a Go project with a PostgreSQL-backed repository.
|
||||
weight: 10
|
||||
---
|
||||
|
||||
## Initialize the project
|
||||
|
||||
Start by creating a Go project.
|
||||
|
||||
```console
|
||||
$ mkdir testcontainers-go-demo
|
||||
$ cd testcontainers-go-demo
|
||||
$ go mod init github.com/testcontainers/testcontainers-go-demo
|
||||
```
|
||||
|
||||
This guide uses the [jackc/pgx](https://github.com/jackc/pgx) PostgreSQL
|
||||
driver to interact with the Postgres database and the testcontainers-go
|
||||
[Postgres module](https://golang.testcontainers.org/modules/postgres/) to
|
||||
spin up a Postgres Docker instance for testing. It also uses
|
||||
[testify](https://github.com/stretchr/testify) for running multiple tests
|
||||
as a suite and for writing assertions.
|
||||
|
||||
Install these dependencies:
|
||||
|
||||
```console
|
||||
$ go get github.com/jackc/pgx/v5
|
||||
$ go get github.com/testcontainers/testcontainers-go
|
||||
$ go get github.com/testcontainers/testcontainers-go/modules/postgres
|
||||
$ go get github.com/stretchr/testify
|
||||
```
|
||||
|
||||
## Create Customer struct
|
||||
|
||||
Create a `types.go` file in the `customer` package and define the `Customer`
|
||||
struct to model the customer details:
|
||||
|
||||
```go
|
||||
package customer
|
||||
|
||||
type Customer struct {
|
||||
Id int
|
||||
Name string
|
||||
Email string
|
||||
}
|
||||
```
|
||||
|
||||
## Create Repository
|
||||
|
||||
Next, create `customer/repo.go`, define the `Repository` struct, and add
|
||||
methods to create a customer and get a customer by email:
|
||||
|
||||
```go
|
||||
package customer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
)
|
||||
|
||||
type Repository struct {
|
||||
conn *pgx.Conn
|
||||
}
|
||||
|
||||
func NewRepository(ctx context.Context, connStr string) (*Repository, error) {
|
||||
conn, err := pgx.Connect(ctx, connStr)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "Unable to connect to database: %v\n", err)
|
||||
return nil, err
|
||||
}
|
||||
return &Repository{
|
||||
conn: conn,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r Repository) CreateCustomer(ctx context.Context, customer Customer) (Customer, error) {
|
||||
err := r.conn.QueryRow(ctx,
|
||||
"INSERT INTO customers (name, email) VALUES ($1, $2) RETURNING id",
|
||||
customer.Name, customer.Email).Scan(&customer.Id)
|
||||
return customer, err
|
||||
}
|
||||
|
||||
func (r Repository) GetCustomerByEmail(ctx context.Context, email string) (Customer, error) {
|
||||
var customer Customer
|
||||
query := "SELECT id, name, email FROM customers WHERE email = $1"
|
||||
err := r.conn.QueryRow(ctx, query, email).
|
||||
Scan(&customer.Id, &customer.Name, &customer.Email)
|
||||
if err != nil {
|
||||
return Customer{}, err
|
||||
}
|
||||
return customer, nil
|
||||
}
|
||||
```
|
||||
|
||||
Here's what the code does:
|
||||
|
||||
- `Repository` holds a `*pgx.Conn` for performing database operations.
|
||||
- `NewRepository(connStr)` takes a database connection string and initializes a `Repository`.
|
||||
- `CreateCustomer()` and `GetCustomerByEmail()` are methods on the `Repository` receiver that insert and query customer records.
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
title: Run tests and next steps
|
||||
linkTitle: Run tests
|
||||
description: Run your Testcontainers-based integration tests and explore next steps.
|
||||
weight: 40
|
||||
---
|
||||
|
||||
## Run the tests
|
||||
|
||||
Run all the tests using `go test ./...`. Optionally add the `-v` flag for
|
||||
verbose output:
|
||||
|
||||
```console
|
||||
$ go test -v ./...
|
||||
```
|
||||
|
||||
You should see two Postgres Docker containers start automatically: one for the
|
||||
suite and its two tests, and another for the initial standalone test. All tests
|
||||
should pass. After the tests finish, the containers are stopped and removed
|
||||
automatically.
|
||||
|
||||
## Summary
|
||||
|
||||
The Testcontainers for Go library helps you write integration tests by using
|
||||
the same type of database (Postgres) that you use in production, instead of
|
||||
mocks. Because you aren't using mocks and instead talk to real services, you're
|
||||
free to refactor code and still verify that the application works as expected.
|
||||
|
||||
To learn more about Testcontainers, visit the
|
||||
[Testcontainers overview](https://testcontainers.com/getting-started/).
|
||||
|
||||
## Further reading
|
||||
|
||||
- [Testcontainers for Go documentation](https://golang.testcontainers.org/)
|
||||
- [Testcontainers for Go quickstart](https://golang.testcontainers.org/quickstart/)
|
||||
- [Testcontainers Postgres module for Go](https://golang.testcontainers.org/modules/postgres/)
|
||||
@@ -0,0 +1,144 @@
|
||||
---
|
||||
title: Reuse containers with test suites
|
||||
linkTitle: Test suites
|
||||
description: Share a single Postgres container across multiple tests using testify suites.
|
||||
weight: 30
|
||||
---
|
||||
|
||||
In the previous section, you saw how to spin up a Postgres Docker container
|
||||
for a single test. But often you have multiple tests in a single file, and you
|
||||
may want to reuse the same Postgres Docker container for all of them.
|
||||
|
||||
You can use the [testify suite](https://pkg.go.dev/github.com/stretchr/testify/suite)
|
||||
package to implement common test setup and teardown actions.
|
||||
|
||||
## Extract container setup
|
||||
|
||||
First, extract the `PostgresContainer` creation logic into a separate file
|
||||
called `testhelpers/containers.go`:
|
||||
|
||||
```go
|
||||
package testhelpers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/testcontainers/testcontainers-go"
|
||||
"github.com/testcontainers/testcontainers-go/modules/postgres"
|
||||
)
|
||||
|
||||
type PostgresContainer struct {
|
||||
*postgres.PostgresContainer
|
||||
ConnectionString string
|
||||
}
|
||||
|
||||
func CreatePostgresContainer(t *testing.T, ctx context.Context) *PostgresContainer {
|
||||
t.Helper()
|
||||
|
||||
ctr, err := postgres.Run(ctx,
|
||||
"postgres:16-alpine",
|
||||
postgres.WithInitScripts(filepath.Join("..", "testdata", "init-db.sql")),
|
||||
postgres.WithDatabase("test-db"),
|
||||
postgres.WithUsername("postgres"),
|
||||
postgres.WithPassword("postgres"),
|
||||
postgres.BasicWaitStrategies(),
|
||||
)
|
||||
testcontainers.CleanupContainer(t, ctr)
|
||||
require.NoError(t, err)
|
||||
|
||||
connStr, err := ctr.ConnectionString(ctx, "sslmode=disable")
|
||||
require.NoError(t, err)
|
||||
|
||||
return &PostgresContainer{
|
||||
PostgresContainer: ctr,
|
||||
ConnectionString: connStr,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In `containers.go`, `PostgresContainer` extends the testcontainers-go
|
||||
`PostgresContainer` to provide easy access to `ConnectionString`. The
|
||||
`CreatePostgresContainer()` function accepts `*testing.T` as its first
|
||||
parameter, calls `t.Helper()` so that test failures point to the caller,
|
||||
and uses `testcontainers.CleanupContainer()` to register automatic cleanup.
|
||||
|
||||
## Write the test suite
|
||||
|
||||
Create `customer/repo_suite_test.go` and implement tests for creating
|
||||
a customer and getting a customer by email using the testify suite package:
|
||||
|
||||
```go
|
||||
package customer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/testcontainers/testcontainers-go-demo/testhelpers"
|
||||
)
|
||||
|
||||
type CustomerRepoTestSuite struct {
|
||||
suite.Suite
|
||||
pgContainer *testhelpers.PostgresContainer
|
||||
repository *Repository
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func (suite *CustomerRepoTestSuite) SetupSuite() {
|
||||
suite.ctx = context.Background()
|
||||
suite.pgContainer = testhelpers.CreatePostgresContainer(suite.T(), suite.ctx)
|
||||
|
||||
repository, err := NewRepository(suite.ctx, suite.pgContainer.ConnectionString)
|
||||
require.NoError(suite.T(), err)
|
||||
suite.repository = repository
|
||||
}
|
||||
|
||||
func (suite *CustomerRepoTestSuite) TestCreateCustomer() {
|
||||
t := suite.T()
|
||||
|
||||
customer, err := suite.repository.CreateCustomer(suite.ctx, Customer{
|
||||
Name: "Henry",
|
||||
Email: "henry@gmail.com",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, customer.Id)
|
||||
}
|
||||
|
||||
func (suite *CustomerRepoTestSuite) TestGetCustomerByEmail() {
|
||||
t := suite.T()
|
||||
|
||||
customer, err := suite.repository.GetCustomerByEmail(suite.ctx, "john@gmail.com")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "John", customer.Name)
|
||||
assert.Equal(t, "john@gmail.com", customer.Email)
|
||||
}
|
||||
|
||||
func TestCustomerRepoTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(CustomerRepoTestSuite))
|
||||
}
|
||||
```
|
||||
|
||||
Here's what the code does:
|
||||
|
||||
- `CustomerRepoTestSuite` extends `suite.Suite` and includes fields shared
|
||||
across multiple tests.
|
||||
- `SetupSuite()` runs once before all tests. It calls
|
||||
`CreatePostgresContainer(suite.T(), ...)` which handles cleanup registration
|
||||
automatically via `CleanupContainer`, so no `TearDownSuite()` is needed.
|
||||
- `TestCreateCustomer()` uses `require.NoError()` for the create operation
|
||||
(fail immediately if it errors) and `assert.NotNil()` for the ID check.
|
||||
- `TestGetCustomerByEmail()` uses `require.NoError()` then asserts on the
|
||||
returned values.
|
||||
- `TestCustomerRepoTestSuite(t *testing.T)` runs the test suite when you
|
||||
execute `go test`.
|
||||
|
||||
> [!TIP]
|
||||
> For the purpose of this guide, the tests don't reset data in the database.
|
||||
> In practice, it's a good idea to reset the database to a known state before
|
||||
> running each test.
|
||||
@@ -0,0 +1,107 @@
|
||||
---
|
||||
title: Write tests with Testcontainers
|
||||
linkTitle: Write tests
|
||||
description: Write your first integration test using testcontainers-go and PostgreSQL.
|
||||
weight: 20
|
||||
---
|
||||
|
||||
You have the `Repository` implementation ready, but for testing you need a
|
||||
PostgreSQL database. You can use testcontainers-go to spin up a Postgres
|
||||
database in a Docker container and run your tests against that database.
|
||||
|
||||
## Set up the test database
|
||||
|
||||
In real applications you might use a database migration tool, but for this
|
||||
guide, use a script to initialize the database.
|
||||
|
||||
Create a `testdata/init-db.sql` file to create the `CUSTOMERS` table and
|
||||
insert sample data:
|
||||
|
||||
```sql
|
||||
CREATE TABLE IF NOT EXISTS customers (id serial, name varchar(255), email varchar(255));
|
||||
|
||||
INSERT INTO customers(name, email) VALUES ('John', 'john@gmail.com');
|
||||
```
|
||||
|
||||
## Understand the testcontainers-go API
|
||||
|
||||
The testcontainers-go library provides the generic `Container` abstraction
|
||||
that can run any containerized service. To further simplify, testcontainers-go
|
||||
provides technology-specific modules that reduce boilerplate and provide a
|
||||
functional options pattern to construct the container instance.
|
||||
|
||||
For example, `PostgresContainer` provides `WithDatabase()`,
|
||||
`WithUsername()`, `WithPassword()`, and other functions to set various
|
||||
properties of Postgres containers.
|
||||
|
||||
## Write the test
|
||||
|
||||
Create the `customer/repo_test.go` file and implement the test:
|
||||
|
||||
```go
|
||||
package customer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/testcontainers/testcontainers-go"
|
||||
"github.com/testcontainers/testcontainers-go/modules/postgres"
|
||||
)
|
||||
|
||||
func TestCustomerRepository(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
ctr, err := postgres.Run(ctx,
|
||||
"postgres:16-alpine",
|
||||
postgres.WithInitScripts(filepath.Join("..", "testdata", "init-db.sql")),
|
||||
postgres.WithDatabase("test-db"),
|
||||
postgres.WithUsername("postgres"),
|
||||
postgres.WithPassword("postgres"),
|
||||
postgres.BasicWaitStrategies(),
|
||||
)
|
||||
testcontainers.CleanupContainer(t, ctr)
|
||||
require.NoError(t, err)
|
||||
|
||||
connStr, err := ctr.ConnectionString(ctx, "sslmode=disable")
|
||||
require.NoError(t, err)
|
||||
|
||||
customerRepo, err := NewRepository(ctx, connStr)
|
||||
require.NoError(t, err)
|
||||
|
||||
c, err := customerRepo.CreateCustomer(ctx, Customer{
|
||||
Name: "Henry",
|
||||
Email: "henry@gmail.com",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, c)
|
||||
|
||||
customer, err := customerRepo.GetCustomerByEmail(ctx, "henry@gmail.com")
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, customer)
|
||||
assert.Equal(t, "Henry", customer.Name)
|
||||
assert.Equal(t, "henry@gmail.com", customer.Email)
|
||||
}
|
||||
```
|
||||
|
||||
Here's what the test does:
|
||||
|
||||
- Calls `postgres.Run()` with the `postgres:16-alpine` Docker image as the
|
||||
first argument. This is the v0.41.0 API — the image is a required positional
|
||||
parameter instead of an option.
|
||||
- Configures initialization scripts using `WithInitScripts(...)` so that the
|
||||
`CUSTOMERS` table is created and sample data is inserted after the database
|
||||
starts.
|
||||
- Uses `postgres.BasicWaitStrategies()` which combines waiting for the Postgres
|
||||
log message and for the port to be ready. This replaces manual wait strategy
|
||||
configuration.
|
||||
- Calls `testcontainers.CleanupContainer(t, ctr)` right after `postgres.Run()`.
|
||||
This registers automatic cleanup with the test framework, replacing the manual
|
||||
`t.Cleanup` and `Terminate` pattern.
|
||||
- Obtains the database `ConnectionString` from the container and initializes a
|
||||
`Repository`.
|
||||
- Creates a customer with the email `henry@gmail.com` and verifies that the
|
||||
customer exists in the database.
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
title: Getting started with Testcontainers for Python
|
||||
linkTitle: Testcontainers for Python
|
||||
description: Learn how to use Testcontainers for Python to test database interactions with a real PostgreSQL instance.
|
||||
keywords: testcontainers, python, testing, postgresql, integration testing, pytest
|
||||
summary: |
|
||||
Learn how to create a Python application and test database interactions
|
||||
using Testcontainers for Python with a real PostgreSQL instance.
|
||||
toc_min: 1
|
||||
toc_max: 2
|
||||
tags: [testing-with-docker]
|
||||
languages: [python]
|
||||
params:
|
||||
time: 15 minutes
|
||||
---
|
||||
|
||||
<!-- Source: https://github.com/testcontainers/tc-guide-getting-started-with-testcontainers-for-python -->
|
||||
|
||||
In this guide, you will learn how to:
|
||||
|
||||
- Create a Python application that uses PostgreSQL to store customer data
|
||||
- Use `psycopg` to interact with the database
|
||||
- Write integration tests using `testcontainers-python` and `pytest`
|
||||
- Manage container lifecycle with pytest fixtures
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Python 3.10+
|
||||
- pip
|
||||
- A Docker environment supported by Testcontainers
|
||||
|
||||
> [!NOTE]
|
||||
> If you're new to Testcontainers, visit the
|
||||
> [Testcontainers overview](https://testcontainers.com/getting-started/) to learn more about
|
||||
> Testcontainers and the benefits of using it.
|
||||
@@ -0,0 +1,130 @@
|
||||
---
|
||||
title: Create the Python project
|
||||
linkTitle: Create the project
|
||||
description: Set up a Python project with a PostgreSQL-backed customer service.
|
||||
weight: 10
|
||||
---
|
||||
|
||||
## Initialize the project
|
||||
|
||||
Start by creating a Python project with a virtual environment:
|
||||
|
||||
```console
|
||||
$ mkdir tc-python-demo
|
||||
$ cd tc-python-demo
|
||||
$ python3 -m venv venv
|
||||
$ source venv/bin/activate
|
||||
```
|
||||
|
||||
This guide uses [psycopg3](https://www.psycopg.org/psycopg3/) to interact
|
||||
with the Postgres database, [pytest](https://pytest.org/) for testing, and
|
||||
[testcontainers-python](https://testcontainers-python.readthedocs.io/) for
|
||||
running a PostgreSQL database in a container.
|
||||
|
||||
Install the dependencies:
|
||||
|
||||
```console
|
||||
$ pip install psycopg pytest testcontainers[postgres]
|
||||
$ pip freeze > requirements.txt
|
||||
```
|
||||
|
||||
The `pip freeze` command generates a `requirements.txt` file so that others
|
||||
can install the same package versions using `pip install -r requirements.txt`.
|
||||
|
||||
## Create the database helper
|
||||
|
||||
Create a `db/connection.py` file with a function to get a database connection:
|
||||
|
||||
```python
|
||||
import os
|
||||
|
||||
import psycopg
|
||||
|
||||
|
||||
def get_connection():
|
||||
host = os.getenv("DB_HOST", "localhost")
|
||||
port = os.getenv("DB_PORT", "5432")
|
||||
username = os.getenv("DB_USERNAME", "postgres")
|
||||
password = os.getenv("DB_PASSWORD", "postgres")
|
||||
database = os.getenv("DB_NAME", "postgres")
|
||||
return psycopg.connect(f"host={host} dbname={database} user={username} password={password} port={port}")
|
||||
```
|
||||
|
||||
Instead of hard-coding the database connection parameters, the function uses
|
||||
environment variables. This makes it possible to run the application in
|
||||
different environments without changing code.
|
||||
|
||||
## Create the business logic
|
||||
|
||||
Create a `customers/customers.py` file and define the `Customer` class:
|
||||
|
||||
```python
|
||||
class Customer:
|
||||
def __init__(self, cust_id, name, email):
|
||||
self.id = cust_id
|
||||
self.name = name
|
||||
self.email = email
|
||||
|
||||
def __str__(self):
|
||||
return f"Customer({self.id}, {self.name}, {self.email})"
|
||||
```
|
||||
|
||||
Add a `create_table()` function to create the `customers` table:
|
||||
|
||||
```python
|
||||
from db.connection import get_connection
|
||||
|
||||
|
||||
def create_table():
|
||||
with get_connection() as conn:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("""
|
||||
CREATE TABLE customers (
|
||||
id serial PRIMARY KEY,
|
||||
name varchar not null,
|
||||
email varchar not null unique)
|
||||
""")
|
||||
conn.commit()
|
||||
```
|
||||
|
||||
The function obtains a database connection using `get_connection()` and creates
|
||||
the `customers` table. The `with` statement automatically closes the connection
|
||||
when done.
|
||||
|
||||
Add the remaining CRUD functions:
|
||||
|
||||
```python
|
||||
def create_customer(name, email):
|
||||
with get_connection() as conn:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"INSERT INTO customers (name, email) VALUES (%s, %s)", (name, email))
|
||||
conn.commit()
|
||||
|
||||
|
||||
def get_all_customers() -> list[Customer]:
|
||||
with get_connection() as conn:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("SELECT * FROM customers")
|
||||
return [Customer(cid, name, email) for cid, name, email in cur]
|
||||
|
||||
|
||||
def get_customer_by_email(email) -> Customer:
|
||||
with get_connection() as conn:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("SELECT id, name, email FROM customers WHERE email = %s", (email,))
|
||||
(cid, name, email) = cur.fetchone()
|
||||
return Customer(cid, name, email)
|
||||
|
||||
|
||||
def delete_all_customers():
|
||||
with get_connection() as conn:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("DELETE FROM customers")
|
||||
conn.commit()
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> To keep it straightforward for this guide, each function creates a new
|
||||
> connection. In a real-world application, use a connection pool to reuse
|
||||
> connections.
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
title: Run tests and next steps
|
||||
linkTitle: Run tests
|
||||
description: Run your Testcontainers-based integration tests and explore next steps.
|
||||
weight: 30
|
||||
---
|
||||
|
||||
## Run the tests
|
||||
|
||||
Run the tests using pytest:
|
||||
|
||||
```console
|
||||
$ pytest -v
|
||||
```
|
||||
|
||||
You should see output similar to:
|
||||
|
||||
```text
|
||||
============================= test session starts ==============================
|
||||
platform linux -- Python 3.13.x, pytest-9.x.x
|
||||
collected 2 items
|
||||
|
||||
tests/test_customers.py::test_get_all_customers PASSED [ 50%]
|
||||
tests/test_customers.py::test_get_customer_by_email PASSED [100%]
|
||||
|
||||
============================== 2 passed in 1.90s ===============================
|
||||
```
|
||||
|
||||
The tests run against a real PostgreSQL database instead of mocks, which gives
|
||||
more confidence in the implementation.
|
||||
|
||||
## Summary
|
||||
|
||||
The Testcontainers for Python library helps you write integration tests using the
|
||||
same type of database (Postgres) that you use in production, instead of mocks.
|
||||
Because you aren't using mocks and instead talk to real services, you're free
|
||||
to refactor code and still verify that the application works as expected.
|
||||
|
||||
In addition to PostgreSQL, Testcontainers for Python provides modules for many
|
||||
SQL databases, NoSQL databases, messaging queues, and more. You can use
|
||||
Testcontainers to run any containerized dependency for your tests.
|
||||
|
||||
To learn more about Testcontainers, visit the
|
||||
[Testcontainers overview](https://testcontainers.com/getting-started/).
|
||||
|
||||
## Further reading
|
||||
|
||||
- [testcontainers-python documentation](https://testcontainers-python.readthedocs.io/)
|
||||
- [Getting started with Testcontainers for Go](/guides/testcontainers-go-getting-started/)
|
||||
- [Getting started with Testcontainers for Java](https://testcontainers.com/guides/getting-started-with-testcontainers-for-java/)
|
||||
- [Getting started with Testcontainers for Node.js](https://testcontainers.com/guides/getting-started-with-testcontainers-for-nodejs/)
|
||||
@@ -0,0 +1,104 @@
|
||||
---
|
||||
title: Write tests with Testcontainers
|
||||
linkTitle: Write tests
|
||||
description: Write integration tests using testcontainers-python and pytest with a real PostgreSQL database.
|
||||
weight: 20
|
||||
---
|
||||
|
||||
You'll create a PostgreSQL container using Testcontainers and use it for all
|
||||
the tests. Before each test, you'll delete all customer records so that tests
|
||||
run with a clean database.
|
||||
|
||||
## Set up pytest fixtures
|
||||
|
||||
This guide uses [pytest fixtures](https://pytest.org/en/stable/how-to/fixtures.html)
|
||||
for setup and teardown logic. A recommended approach is to use
|
||||
[finalizers](https://pytest.org/en/stable/how-to/fixtures.html#adding-finalizers-directly)
|
||||
to guarantee cleanup runs even if setup fails:
|
||||
|
||||
```python
|
||||
@pytest.fixture
|
||||
def setup(request):
|
||||
# setup code
|
||||
|
||||
def cleanup():
|
||||
# teardown code
|
||||
|
||||
request.addfinalizer(cleanup)
|
||||
return some_value
|
||||
```
|
||||
|
||||
## Create the test file
|
||||
|
||||
Create a `tests/__init__.py` file with empty content to enable pytest
|
||||
[auto-discovery](https://pytest.org/explanation/goodpractices.html#test-discovery).
|
||||
|
||||
Then create `tests/test_customers.py` with the fixtures:
|
||||
|
||||
```python
|
||||
import os
|
||||
import pytest
|
||||
from testcontainers.postgres import PostgresContainer
|
||||
|
||||
from customers import customers
|
||||
|
||||
postgres = PostgresContainer("postgres:16-alpine")
|
||||
|
||||
|
||||
@pytest.fixture(scope="module", autouse=True)
|
||||
def setup(request):
|
||||
postgres.start()
|
||||
|
||||
def remove_container():
|
||||
postgres.stop()
|
||||
|
||||
request.addfinalizer(remove_container)
|
||||
os.environ["DB_CONN"] = postgres.get_connection_url()
|
||||
os.environ["DB_HOST"] = postgres.get_container_host_ip()
|
||||
os.environ["DB_PORT"] = str(postgres.get_exposed_port(5432))
|
||||
os.environ["DB_USERNAME"] = postgres.username
|
||||
os.environ["DB_PASSWORD"] = postgres.password
|
||||
os.environ["DB_NAME"] = postgres.dbname
|
||||
customers.create_table()
|
||||
|
||||
|
||||
@pytest.fixture(scope="function", autouse=True)
|
||||
def setup_data():
|
||||
customers.delete_all_customers()
|
||||
```
|
||||
|
||||
Here's what the fixtures do:
|
||||
|
||||
- The `setup` fixture has `scope="module"`, so it runs once for all tests in
|
||||
the file. It starts a PostgreSQL container, sets environment variables with
|
||||
the connection details, and creates the `customers` table. A cleanup
|
||||
function removes the container after all tests complete.
|
||||
- The `setup_data` fixture has `scope="function"`, so it runs before every
|
||||
test. It deletes all records to give each test a clean database.
|
||||
|
||||
## Write the tests
|
||||
|
||||
Add the test functions to the same file:
|
||||
|
||||
```python
|
||||
def test_get_all_customers():
|
||||
customers.create_customer("Siva", "siva@gmail.com")
|
||||
customers.create_customer("James", "james@gmail.com")
|
||||
customers_list = customers.get_all_customers()
|
||||
assert len(customers_list) == 2
|
||||
|
||||
|
||||
def test_get_customer_by_email():
|
||||
customers.create_customer("John", "john@gmail.com")
|
||||
customer = customers.get_customer_by_email("john@gmail.com")
|
||||
assert customer.name == "John"
|
||||
assert customer.email == "john@gmail.com"
|
||||
```
|
||||
|
||||
- `test_get_all_customers()` inserts two customer records, fetches all
|
||||
customers, and asserts the count.
|
||||
- `test_get_customer_by_email()` inserts a customer, fetches it by email, and
|
||||
asserts the details.
|
||||
|
||||
Because `setup_data` deletes all records before each test, the tests can run in
|
||||
any order.
|
||||
@@ -2,29 +2,30 @@
|
||||
title: Testcontainers
|
||||
weight: 40
|
||||
description: Learn how to use Testcontainers to run containers programmatically in your preferred programming language.
|
||||
keywords: docker APIs, docker, testcontainers documentation, testcontainers, testcontainers oss, testcontainers oss documentation,
|
||||
keywords:
|
||||
docker APIs, docker, testcontainers documentation, testcontainers, testcontainers oss, testcontainers oss documentation,
|
||||
docker compose, docker-compose, java, golang, go
|
||||
params:
|
||||
sidebar:
|
||||
group: Open source
|
||||
intro:
|
||||
- title: What is Testcontainers?
|
||||
description: Learn about what Testcontainers does and its key benefits
|
||||
icon: feature_search
|
||||
link: https://testcontainers.com/getting-started/#what-is-testcontainers
|
||||
- title: The Testcontainers workflow
|
||||
description: Understand the Testcontainers workflow
|
||||
icon: explore
|
||||
link: https://testcontainers.com/getting-started/#testcontainers-workflow
|
||||
- title: What is Testcontainers?
|
||||
description: Learn about what Testcontainers does and its key benefits
|
||||
icon: feature_search
|
||||
link: https://testcontainers.com/getting-started/#what-is-testcontainers
|
||||
- title: The Testcontainers workflow
|
||||
description: Understand the Testcontainers workflow
|
||||
icon: explore
|
||||
link: https://testcontainers.com/getting-started/#testcontainers-workflow
|
||||
quickstart:
|
||||
- title: Testcontainers for Go
|
||||
description: A Go package that makes it simple to create and clean up container-based dependencies for automated integration/smoke tests.
|
||||
icon: /icons/go.svg
|
||||
link: https://golang.testcontainers.org/quickstart/
|
||||
- title: Testcontainers for Java
|
||||
description: A Java library that supports JUnit tests, providing lightweight, throwaway instances of anything that can run in a Docker container.
|
||||
icon: /icons/java.svg
|
||||
link: https://java.testcontainers.org/
|
||||
- title: Testcontainers for Go
|
||||
description: A Go package that makes it simple to create and clean up container-based dependencies for automated integration/smoke tests.
|
||||
icon: /icons/go.svg
|
||||
link: https://golang.testcontainers.org/quickstart/
|
||||
- title: Testcontainers for Java
|
||||
description: A Java library that supports JUnit tests, providing lightweight, throwaway instances of anything that can run in a Docker container.
|
||||
icon: /icons/java.svg
|
||||
link: https://java.testcontainers.org/
|
||||
---
|
||||
|
||||
Testcontainers is a set of open source libraries that provides easy and lightweight APIs for bootstrapping local development and test dependencies with real services wrapped in Docker containers.
|
||||
@@ -32,6 +33,14 @@ Using Testcontainers, you can write tests that depend on the same services you u
|
||||
|
||||
{{< grid items=intro >}}
|
||||
|
||||
## Guides
|
||||
|
||||
Explore hands-on Testcontainers guides to learn how to use Testcontainers
|
||||
with different languages and popular frameworks:
|
||||
|
||||
- [Getting started with Testcontainers for Go](/guides/testcontainers-go-getting-started/)
|
||||
- [Getting started with Testcontainers for Python](/guides/testcontainers-python-getting-started/)
|
||||
|
||||
## Quickstart
|
||||
|
||||
### Supported languages
|
||||
@@ -54,6 +63,6 @@ However, these are not actively tested in the main development workflow, so not
|
||||
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,
|
||||
contact the Testcontainers team and other users from the Testcontainers community on [Slack](https://slack.testcontainers.org/).
|
||||
contact the Testcontainers team and other users from the Testcontainers community on [Slack](https://slack.testcontainers.org/).
|
||||
|
||||
{{< grid items=quickstart >}}
|
||||
{{< grid items=quickstart >}}
|
||||
|
||||
@@ -36,5 +36,7 @@ release-notes:
|
||||
title: Release notes
|
||||
secrets:
|
||||
title: Secrets
|
||||
testing-with-docker:
|
||||
title: Testing with Docker
|
||||
troubleshooting:
|
||||
title: Troubleshooting
|
||||
|
||||
Reference in New Issue
Block a user