Docker for Test Environments
Docker enables completely reproducible test environments — the same container that runs on your laptop runs identically in GitHub Actions and Jenkins. Docker Compose makes it trivial to spin up the full application stack (app + database + dependencies) for integration testing, isolating your tests from shared infrastructure.
Docker Test Environment Setup
# ══════════════════════════════════════════════════════════════
# docker-compose.test.yml
# Spins up: App + API + Database for integration testing
# ══════════════════════════════════════════════════════════════
version: '3.8'
services:
# Test Database (fresh, isolated for testing)
test-db:
image: postgres:16-alpine
environment:
POSTGRES_DB: testdb
POSTGRES_USER: testuser
POSTGRES_PASSWORD: testpass
ports:
- "5433:5432" # Different port to avoid conflict with local dev DB
healthcheck:
test: ["CMD-SHELL", "pg_isready -U testuser"]
interval: 5s
timeout: 3s
retries: 10
# API Service (your application under test)
api:
build: .
ports:
- "3001:3000"
environment:
DATABASE_URL: "postgresql://testuser:testpass@test-db:5432/testdb"
NODE_ENV: test
JWT_SECRET: test-secret-key-not-for-production
depends_on:
test-db:
condition: service_healthy # Wait for DB to be ready
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 10s
timeout: 5s
retries: 5
# Test Runner (your automation suite)
test-runner:
build:
context: .
dockerfile: Dockerfile.test
environment:
BASE_URL: http://api:3000 # Internal Docker network URL
API_TOKEN: test-token-123
depends_on:
api:
condition: service_healthy # Wait for API to be ready
volumes:
- ./results:/app/results # Mount results directory
- ./allure-results:/app/allure-results
# Run with:
# docker-compose -f docker-compose.test.yml up --build --abort-on-container-exit
# Check exit code: $? (0 = all tests passed)
# ══════════════════════════════════════════════════════════════
# Dockerfile.test — Test runner image
# ══════════════════════════════════════════════════════════════
# FROM python:3.12-slim
# WORKDIR /app
# Install system deps for Selenium headless:
# RUN apt-get update && apt-get install -y # chromium chromium-driver # && rm -rf /var/lib/apt/lists/*
# COPY requirements-test.txt .
# RUN pip install --no-cache-dir -r requirements-test.txt
# COPY tests/ tests/
# COPY conftest.py .
# CMD ["pytest", "tests/", "-v", # "--alluredir=allure-results", # "--junitxml=results/test-results.xml"]
# ══════════════════════════════════════════════════════════════
# GITHUB ACTIONS with Docker Compose
# ══════════════════════════════════════════════════════════════
# - name: Run Integration Tests in Docker
# run: |
# docker-compose -f docker-compose.test.yml up --build --abort-on-container-exit
# TEST_EXIT_CODE=$?
# docker-compose -f docker-compose.test.yml down
# exit $TEST_EXIT_CODECommon Mistakes
- Starting tests before services are healthy — always use healthcheck + condition: service_healthy in depends_on; a 500ms head start isn't enough for database initialization
- Not mounting results directories — test results inside a container disappear when the container stops; mount /app/results to host for CI artifact collection
- Using the same DB port as local development — use distinct ports (5433 for test, 5432 for dev) to avoid connection conflicts
- Not cleaning up containers after CI — always run docker-compose down after tests to free CI runner resources; use trap in shell scripts
Tip
Tip
Practice Docker for Test Environments in small, isolated examples before integrating into larger projects. Breaking concepts into small experiments builds genuine understanding faster than reading alone.
Technical diagram.
Practice Task
Note
Practice Task — (1) Write a working example of Docker for Test Environments from scratch without looking at notes. (2) Modify it to handle an edge case (empty input, null value, or error state). (3) Share your solution in the Priygop community for feedback.
Quick Quiz
Common Mistake
Warning
A common mistake with Docker for Test Environments is skipping edge case testing — empty inputs, null values, and unexpected data types. Always validate boundary conditions to write robust, production-ready software testing code.
Key Takeaways
- Docker enables completely reproducible test environments — the same container that runs on your laptop runs identically in GitHub Actions and Jenkins.
- Starting tests before services are healthy — always use healthcheck + condition: service_healthy in depends_on; a 500ms head start isn't enough for database initialization
- Not mounting results directories — test results inside a container disappear when the container stops; mount /app/results to host for CI artifact collection
- Using the same DB port as local development — use distinct ports (5433 for test, 5432 for dev) to avoid connection conflicts