
What are Testcontainers
Testcontainers is an open source library that allows you to test any containerized dependencies, such as databases, various cloud technologies, or message brokers. For ease of use, Testcontainers has many preconfigured dependencies called modules.
Besides that, Testcontainers supports various languages in which you can easily write your tests, such as Python, Go, Rust, Ruby, JavaScript, .NET, Java, and others.
Common use cases with Testcontainers
Thanks to the container technology, you can obtain fresh, clean instances without any complex setup for use cases such as:
- Data access layer integration tests
- UI/Acceptance tests
- Application integration tests
Setup Testcontainers with Podman
Before we start, you need to have installed Podman and run it in socket listening:
$ podman system service --time=0 &
Set Testcontainers runtime to Podman by using one of the following options:
-
Enable the Docker Compatibility feature.
-
Set the
DOCKER_HOSTenvironment variable to your Podman socket:-
MacOS
$ export DOCKER_HOST=unix://$(podman machine inspect --format '{{.ConnectionInfo.PodmanSocket.Path}}')$ export TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE=/var/run/docker.sock -
Linux:
$ export DOCKER_HOST=unix://${XDG_RUNTIME_DIR}/podman/podman.sock
noteYou can also configure Testcontainers globally with a
.testcontainers.propertiesfile in your home directory. That file does not run shell commands or expand environment variables, so write the resolved socket path rather than a command.For Java,
docker.hostin that file also requiresdocker.client.strategy=org.testcontainers.dockerclient.EnvironmentAndSystemPropertyClientProviderStrategy. -
OPTIONAL: If you are running Podman in rootless mode, you must disable Ryuk by defining this environment variable:
$ export TESTCONTAINERS_RYUK_DISABLED=true
Creating a project
This example uses the Redis service and Redis module from Testcontainers. You can create a project and install all the dependencies by following the procedure.
-
Initialize a project.
$ npm init -y -
Installing dependencies.
$ npm install testcontainers vitest @testcontainers/redis redis typescript --save-dev -
Update
package.jsonfile.package.json..."scripts": {"test": "vitest"},... -
Create basic CRUD operations using the Redis Node.js library.
index.tsimport { createClient, RedisClientType } from 'redis';let redisClient: RedisClientType | undefined = undefined;export async function connectRedis(url: string) {redisClient = createClient({ url });await redisClient.connect();return redisClient;}export async function setValue(key: string, value: string): Promise<string | null> {if (!redisClient) {throw new Error('Redis client is not connected');}return await redisClient.set(key, value);}export async function getValue(key: string): Promise<string | null> {if (!redisClient) {throw new Error('Redis client is not connected');}return redisClient.get(key);}export async function deleteValue(key: string[]): Promise<number> {if (!redisClient) {throw new Error('Redis client is not connected');}return await redisClient.del(key);}export async function disconnectRedis() {if (redisClient) {await redisClient.quit();redisClient = undefined;}} -
Create basic tests for CRUD operations.
index.spec.tsimport { afterAll, beforeAll, beforeEach, expect, test } from 'vitest';import { connectRedis, deleteValue, disconnectRedis, getValue, setValue } from '.';import { RedisContainer, StartedRedisContainer } from '@testcontainers/redis';import { Wait } from 'testcontainers';import { createClient } from 'redis';let container: StartedRedisContainer;beforeAll(async () => {container = await new RedisContainer().withExposedPorts(6379).withWaitStrategy(Wait.forLogMessage('Ready to accept connections')).start();await connectRedis(`redis://localhost:${container.getMappedPort(6379)}`);});afterAll(async () => {await disconnectRedis();});beforeEach(async () => {// Flushind DB and adding to Redis some values before each testconst client = createClient({ url: `redis://localhost:${container.getMappedPort(6379)}` });await client.connect();await client.flushDb();await client.set('preset-key', 'preset-value');await client.set('preset-key1', 'preset-value1');await client.quit();});test('set value on server', async () => {// Set valueconst ret = await setValue('key', 'value');expect(ret).toBe('OK');// Update valueconst ret1 = await setValue('key', 'updated-value');expect(ret1).toBe('OK');});test('get value from server', async () => {// Get preset valueconst value = await getValue('preset-key');expect(value).toBe('preset-value');// Get not existing valueconst value1 = await getValue('key');expect(value1).toBeNull();});test('delete value on server', async () => {// Delete two records in a same timeconst res = await deleteValue(['preset-key', 'preset-key1']);expect(res).toBe(2);// Delete not existing recordconst res1 = await deleteValue(['key']);expect(res1).toBe(0);});
Running tests
When running Testcontainers for the first time, ensure that you run your tests in DEBUG mode by using this command:
$ DEBUG=testcontainers* npm test
Then, you should be able to see a line similar to the one below:
testcontainers [DEBUG] Found custom configuration: dockerHost: "unix:///run/user/1000/podman/podman.sock"
This line indicates that Testcontainers picked up your Podman socket, and the containers are created on the Podman engine instead of Docker.
Conclusion
This tutorial provides a basic step-by-step walkthrough using the Testcontainers technology to run a Redis server with Podman. More examples can be found in the guides of Testcontainers. If you encounter any problems, feel free to open an issue on Podman Desktop's GitHub.