Enhancing Your Testing Workflow with Testcontainers

Gerardo Lopez Falcón
3 min readJul 5, 2024

--

Introduction

In the world of software development, ensuring the reliability and stability of your applications is paramount. However, setting up consistent and isolated environments for integration tests can be a daunting task. This is where Testcontainers come into play. In this blog post, we will explore what Testcontainers are, why you should use them, and walk through a practical example with code.

What Are Testcontainers?

Testcontainers is a library that provides lightweight, disposable instances of common databases, web browsers, or any other services that can run in a Docker container. These containers are ideal for testing purposes, as they ensure a consistent and isolated environment for running your tests. Testcontainers can be used with Java, Python, and several other languages, making them versatile for various development stacks.

Why Use Testcontainers?

  1. Consistency: By running tests in isolated containers, you can avoid the “works on my machine” problem. Each test run starts with a clean state, ensuring consistency.
  2. Integration Testing: Testcontainers allow you to spin up real instances of the services your application depends on, facilitating thorough integration testing.
  3. Simplified Setup: Instead of managing complex setup and teardown logic in your tests, you can use Testcontainers to handle this automatically with Docker.
  4. Parallel Testing: Containers can run in parallel without interfering with each other, speeding up your testing process.

Practical Example with Code

Let’s go through a practical example using Python with the pytest framework and the testcontainers library to test an application that interacts with a PostgreSQL database.

Adding Dependencies

First, you need to install the necessary dependencies. You can do this using pip:

pip install pytest pytest-django testcontainers[postgresql] psycopg2-binary

Next, set up a Testcontainer for PostgreSQL in your test file.

Step 1: Create a Django Project

We’ll assume you have a Django project set up. If not, you can create one using:

django-admin startproject myproject
cd myproject
django-admin startapp myapp

Step 2: Configure Django for Testing

Edit your myproject/settings.py to include the necessary configurations for testing:

# settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'test',
'USER': 'test',
'PASSWORD': 'test',
'HOST': 'localhost',
'PORT': '5432',
}
}
INSTALLED_APPS = [
...
'myapp',
]
# Additional settings for your project

Step 3: Write the Test with Testcontainers

Create a test file test_db.py in your myapp directory:

# myapp/test_db.py

import pytest
from django.db import connections
from django.db.utils import OperationalError
from testcontainers.postgres import PostgresContainer
@pytest.fixture(scope='module')
def postgres_container():
container = PostgresContainer("postgres:13.3")
container.start()
yield container
container.stop()
@pytest.fixture(scope='module')
def django_db_setup(postgres_container):
from django.conf import settings
settings.DATABASES['default'] = {
'ENGINE': 'django.db.backends.postgresql',
'NAME': postgres_container.MYSQL_DATABASE,
'USER': postgres_container.MYSQL_USER,
'PASSWORD': postgres_container.MYSQL_PASSWORD,
'HOST': postgres_container.get_container_host_ip(),
'PORT': postgres_container.get_exposed_port(5432),
}

def test_can_connect_to_database():
db_conn = connections['default']
try:
db_conn.ensure_connection()
assert db_conn.is_usable()
except OperationalError:
pytest.fail("Database is not available")

Explanation

  1. Dependencies: Install pytest, pytest-django, testcontainers[postgresql], and psycopg2-binary for PostgreSQL database interaction.
  2. Container Setup: A PostgreSQL container is created and started using the PostgresContainer class from testcontainers.
  3. Django DB Setup: A fixture django_db_setup is used to configure Django to use the PostgreSQL container.
  4. Test Method: The test test_can_connect_to_database ensures that a connection to the database can be established.

Running the Test

Run the test using pytest:

pytest myapp/test_db.py

Conclusion

Testcontainers provide a robust and straightforward way to enhance your testing workflow by ensuring isolated, consistent environments for running integration tests. This example demonstrated how to set up and use Testcontainers with a Django application to test database interactions. By incorporating Testcontainers into your testing strategy, you can achieve more reliable and maintainable tests.

Additional Resources

Feel free to explore these resources to further enhance your testing capabilities with Testcontainers in Python.

If you want to test locally without worrying about containers using too much resources, or simplify Testcontainers setup in CI, check out Testcontainers Cloud: https://testcontainers.com/cloud/

Happy testing!

--

--

Gerardo Lopez Falcón
Gerardo Lopez Falcón

Written by Gerardo Lopez Falcón

Google Developer Expert & Sr Software Engineer & DevOps &. Soccer Fan

No responses yet