Skip to content

Commit 36d384d

Browse files
authored
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
1 parent 09842d4 commit 36d384d

File tree

13 files changed

+1094
-19
lines changed

13 files changed

+1094
-19
lines changed

.claude/skills/testcontainers-guides-migrator/SKILL.md

Lines changed: 315 additions & 0 deletions
Large diffs are not rendered by default.

_vale/config/vocabularies/Docker/accept.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,14 @@ Paketo
167167
PAT
168168
perl
169169
pgAdmin
170+
pgx
170171
PKG
171172
plaintext
172173
plist
173174
pluggable
174175
Postgres
176+
psycopg
177+
pytest
175178
PowerShell
176179
Python
177180
Qualcomm
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
title: Getting started with Testcontainers for Go
3+
linkTitle: Testcontainers for Go
4+
description: Learn how to use Testcontainers for Go to test database interactions with a real PostgreSQL instance.
5+
keywords: testcontainers, go, golang, testing, postgresql, integration testing
6+
summary: |
7+
Learn how to create a Go application and test database interactions
8+
using Testcontainers for Go with a real PostgreSQL instance.
9+
toc_min: 1
10+
toc_max: 2
11+
tags: [testing-with-docker]
12+
languages: [go]
13+
params:
14+
time: 20 minutes
15+
---
16+
17+
<!-- Source: https://github.com/testcontainers/tc-guide-getting-started-with-testcontainers-for-go -->
18+
19+
In this guide, you will learn how to:
20+
21+
- Create a Go application with modules support
22+
- Implement a Repository to manage customer data in a PostgreSQL database using the pgx driver
23+
- Write integration tests using testcontainers-go
24+
- Reuse containers across multiple tests using test suites
25+
26+
## Prerequisites
27+
28+
- Go 1.25+
29+
- Your preferred IDE (VS Code, GoLand)
30+
- A Docker environment supported by Testcontainers. For details, see
31+
the [testcontainers-go system requirements](https://golang.testcontainers.org/system_requirements/).
32+
33+
> [!NOTE]
34+
> If you're new to Testcontainers, visit the
35+
> [Testcontainers overview](https://testcontainers.com/getting-started/) to learn more about
36+
> Testcontainers and the benefits of using it.
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
---
2+
title: Create the Go project
3+
linkTitle: Create the project
4+
description: Set up a Go project with a PostgreSQL-backed repository.
5+
weight: 10
6+
---
7+
8+
## Initialize the project
9+
10+
Start by creating a Go project.
11+
12+
```console
13+
$ mkdir testcontainers-go-demo
14+
$ cd testcontainers-go-demo
15+
$ go mod init github.com/testcontainers/testcontainers-go-demo
16+
```
17+
18+
This guide uses the [jackc/pgx](https://github.com/jackc/pgx) PostgreSQL
19+
driver to interact with the Postgres database and the testcontainers-go
20+
[Postgres module](https://golang.testcontainers.org/modules/postgres/) to
21+
spin up a Postgres Docker instance for testing. It also uses
22+
[testify](https://github.com/stretchr/testify) for running multiple tests
23+
as a suite and for writing assertions.
24+
25+
Install these dependencies:
26+
27+
```console
28+
$ go get github.com/jackc/pgx/v5
29+
$ go get github.com/testcontainers/testcontainers-go
30+
$ go get github.com/testcontainers/testcontainers-go/modules/postgres
31+
$ go get github.com/stretchr/testify
32+
```
33+
34+
## Create Customer struct
35+
36+
Create a `types.go` file in the `customer` package and define the `Customer`
37+
struct to model the customer details:
38+
39+
```go
40+
package customer
41+
42+
type Customer struct {
43+
Id int
44+
Name string
45+
Email string
46+
}
47+
```
48+
49+
## Create Repository
50+
51+
Next, create `customer/repo.go`, define the `Repository` struct, and add
52+
methods to create a customer and get a customer by email:
53+
54+
```go
55+
package customer
56+
57+
import (
58+
"context"
59+
"fmt"
60+
"os"
61+
62+
"github.com/jackc/pgx/v5"
63+
)
64+
65+
type Repository struct {
66+
conn *pgx.Conn
67+
}
68+
69+
func NewRepository(ctx context.Context, connStr string) (*Repository, error) {
70+
conn, err := pgx.Connect(ctx, connStr)
71+
if err != nil {
72+
_, _ = fmt.Fprintf(os.Stderr, "Unable to connect to database: %v\n", err)
73+
return nil, err
74+
}
75+
return &Repository{
76+
conn: conn,
77+
}, nil
78+
}
79+
80+
func (r Repository) CreateCustomer(ctx context.Context, customer Customer) (Customer, error) {
81+
err := r.conn.QueryRow(ctx,
82+
"INSERT INTO customers (name, email) VALUES ($1, $2) RETURNING id",
83+
customer.Name, customer.Email).Scan(&customer.Id)
84+
return customer, err
85+
}
86+
87+
func (r Repository) GetCustomerByEmail(ctx context.Context, email string) (Customer, error) {
88+
var customer Customer
89+
query := "SELECT id, name, email FROM customers WHERE email = $1"
90+
err := r.conn.QueryRow(ctx, query, email).
91+
Scan(&customer.Id, &customer.Name, &customer.Email)
92+
if err != nil {
93+
return Customer{}, err
94+
}
95+
return customer, nil
96+
}
97+
```
98+
99+
Here's what the code does:
100+
101+
- `Repository` holds a `*pgx.Conn` for performing database operations.
102+
- `NewRepository(connStr)` takes a database connection string and initializes a `Repository`.
103+
- `CreateCustomer()` and `GetCustomerByEmail()` are methods on the `Repository` receiver that insert and query customer records.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
title: Run tests and next steps
3+
linkTitle: Run tests
4+
description: Run your Testcontainers-based integration tests and explore next steps.
5+
weight: 40
6+
---
7+
8+
## Run the tests
9+
10+
Run all the tests using `go test ./...`. Optionally add the `-v` flag for
11+
verbose output:
12+
13+
```console
14+
$ go test -v ./...
15+
```
16+
17+
You should see two Postgres Docker containers start automatically: one for the
18+
suite and its two tests, and another for the initial standalone test. All tests
19+
should pass. After the tests finish, the containers are stopped and removed
20+
automatically.
21+
22+
## Summary
23+
24+
The Testcontainers for Go library helps you write integration tests by using
25+
the same type of database (Postgres) that you use in production, instead of
26+
mocks. Because you aren't using mocks and instead talk to real services, you're
27+
free to refactor code and still verify that the application works as expected.
28+
29+
To learn more about Testcontainers, visit the
30+
[Testcontainers overview](https://testcontainers.com/getting-started/).
31+
32+
## Further reading
33+
34+
- [Testcontainers for Go documentation](https://golang.testcontainers.org/)
35+
- [Testcontainers for Go quickstart](https://golang.testcontainers.org/quickstart/)
36+
- [Testcontainers Postgres module for Go](https://golang.testcontainers.org/modules/postgres/)
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
---
2+
title: Reuse containers with test suites
3+
linkTitle: Test suites
4+
description: Share a single Postgres container across multiple tests using testify suites.
5+
weight: 30
6+
---
7+
8+
In the previous section, you saw how to spin up a Postgres Docker container
9+
for a single test. But often you have multiple tests in a single file, and you
10+
may want to reuse the same Postgres Docker container for all of them.
11+
12+
You can use the [testify suite](https://pkg.go.dev/github.com/stretchr/testify/suite)
13+
package to implement common test setup and teardown actions.
14+
15+
## Extract container setup
16+
17+
First, extract the `PostgresContainer` creation logic into a separate file
18+
called `testhelpers/containers.go`:
19+
20+
```go
21+
package testhelpers
22+
23+
import (
24+
"context"
25+
"path/filepath"
26+
"testing"
27+
28+
"github.com/stretchr/testify/require"
29+
"github.com/testcontainers/testcontainers-go"
30+
"github.com/testcontainers/testcontainers-go/modules/postgres"
31+
)
32+
33+
type PostgresContainer struct {
34+
*postgres.PostgresContainer
35+
ConnectionString string
36+
}
37+
38+
func CreatePostgresContainer(t *testing.T, ctx context.Context) *PostgresContainer {
39+
t.Helper()
40+
41+
ctr, err := postgres.Run(ctx,
42+
"postgres:16-alpine",
43+
postgres.WithInitScripts(filepath.Join("..", "testdata", "init-db.sql")),
44+
postgres.WithDatabase("test-db"),
45+
postgres.WithUsername("postgres"),
46+
postgres.WithPassword("postgres"),
47+
postgres.BasicWaitStrategies(),
48+
)
49+
testcontainers.CleanupContainer(t, ctr)
50+
require.NoError(t, err)
51+
52+
connStr, err := ctr.ConnectionString(ctx, "sslmode=disable")
53+
require.NoError(t, err)
54+
55+
return &PostgresContainer{
56+
PostgresContainer: ctr,
57+
ConnectionString: connStr,
58+
}
59+
}
60+
```
61+
62+
In `containers.go`, `PostgresContainer` extends the testcontainers-go
63+
`PostgresContainer` to provide easy access to `ConnectionString`. The
64+
`CreatePostgresContainer()` function accepts `*testing.T` as its first
65+
parameter, calls `t.Helper()` so that test failures point to the caller,
66+
and uses `testcontainers.CleanupContainer()` to register automatic cleanup.
67+
68+
## Write the test suite
69+
70+
Create `customer/repo_suite_test.go` and implement tests for creating
71+
a customer and getting a customer by email using the testify suite package:
72+
73+
```go
74+
package customer
75+
76+
import (
77+
"context"
78+
"testing"
79+
80+
"github.com/stretchr/testify/assert"
81+
"github.com/stretchr/testify/require"
82+
"github.com/stretchr/testify/suite"
83+
"github.com/testcontainers/testcontainers-go-demo/testhelpers"
84+
)
85+
86+
type CustomerRepoTestSuite struct {
87+
suite.Suite
88+
pgContainer *testhelpers.PostgresContainer
89+
repository *Repository
90+
ctx context.Context
91+
}
92+
93+
func (suite *CustomerRepoTestSuite) SetupSuite() {
94+
suite.ctx = context.Background()
95+
suite.pgContainer = testhelpers.CreatePostgresContainer(suite.T(), suite.ctx)
96+
97+
repository, err := NewRepository(suite.ctx, suite.pgContainer.ConnectionString)
98+
require.NoError(suite.T(), err)
99+
suite.repository = repository
100+
}
101+
102+
func (suite *CustomerRepoTestSuite) TestCreateCustomer() {
103+
t := suite.T()
104+
105+
customer, err := suite.repository.CreateCustomer(suite.ctx, Customer{
106+
Name: "Henry",
107+
Email: "henry@gmail.com",
108+
})
109+
require.NoError(t, err)
110+
assert.NotNil(t, customer.Id)
111+
}
112+
113+
func (suite *CustomerRepoTestSuite) TestGetCustomerByEmail() {
114+
t := suite.T()
115+
116+
customer, err := suite.repository.GetCustomerByEmail(suite.ctx, "john@gmail.com")
117+
require.NoError(t, err)
118+
assert.Equal(t, "John", customer.Name)
119+
assert.Equal(t, "john@gmail.com", customer.Email)
120+
}
121+
122+
func TestCustomerRepoTestSuite(t *testing.T) {
123+
suite.Run(t, new(CustomerRepoTestSuite))
124+
}
125+
```
126+
127+
Here's what the code does:
128+
129+
- `CustomerRepoTestSuite` extends `suite.Suite` and includes fields shared
130+
across multiple tests.
131+
- `SetupSuite()` runs once before all tests. It calls
132+
`CreatePostgresContainer(suite.T(), ...)` which handles cleanup registration
133+
automatically via `CleanupContainer`, so no `TearDownSuite()` is needed.
134+
- `TestCreateCustomer()` uses `require.NoError()` for the create operation
135+
(fail immediately if it errors) and `assert.NotNil()` for the ID check.
136+
- `TestGetCustomerByEmail()` uses `require.NoError()` then asserts on the
137+
returned values.
138+
- `TestCustomerRepoTestSuite(t *testing.T)` runs the test suite when you
139+
execute `go test`.
140+
141+
> [!TIP]
142+
> For the purpose of this guide, the tests don't reset data in the database.
143+
> In practice, it's a good idea to reset the database to a known state before
144+
> running each test.

0 commit comments

Comments
 (0)