Docker Basics for QA
Why Docker Matters for QA
Test environments are increasingly containerized. Docker provides isolated, reproducible environments that work the same on your laptop, in CI, and on a colleague's machine. Basic Docker skills are non-negotiable for modern QA work.
Core Concepts
| Concept | What It Is | Analogy |
|---|---|---|
| Image | A blueprint for a container (read-only) | A class definition |
| Container | A running instance of an image | An object instantiated from a class |
| Dockerfile | Instructions to build an image | A recipe |
| Volume | Persistent storage that survives container removal | An external hard drive |
| Network | Virtual network connecting containers | A local network switch |
| Registry | A repository for images (Docker Hub, ECR, GCR) | npm registry for packages |
Running Containers
Basic Commands
# Run a container (pull image if not local, start, attach)
docker run -d --name test-db \
-e POSTGRES_PASSWORD=testpass \
-e POSTGRES_DB=test_db \
-p 5432:5432 \
postgres:16
# Flag breakdown:
# -d Run in background (detached)
# --name test-db Give it a meaningful name
# -e KEY=VALUE Set environment variables
# -p 5432:5432 Map host port to container port (host:container)
# postgres:16 Image name and tag
Managing Containers
# List running containers
docker ps
# List all containers (including stopped)
docker ps -a
# Stop a container
docker stop test-db
# Start a stopped container
docker start test-db
# Restart a container
docker restart test-db
# Remove a stopped container
docker rm test-db
# Stop and remove in one step
docker stop test-db && docker rm test-db
# Remove all stopped containers
docker container prune
Viewing Logs
# View all container logs
docker logs test-db
# View last 50 lines
docker logs test-db --tail 50
# Follow logs in real-time (like tail -f)
docker logs test-db --follow
# Logs with timestamps
docker logs test-db --timestamps
# Combine: last 50 lines + follow
docker logs test-db --tail 50 --follow
Executing Commands in Containers
# Open an interactive shell in a running container
docker exec -it test-db bash
# Run a specific command
docker exec test-db psql -U postgres -d test_db -c "SELECT count(*) FROM users;"
# Run a command in a running container (non-interactive)
docker exec test-db pg_isready
Common QA Containers
Database Containers
# PostgreSQL
docker run -d --name test-postgres \
-e POSTGRES_PASSWORD=testpass \
-e POSTGRES_DB=test_db \
-p 5432:5432 \
postgres:16
# MySQL
docker run -d --name test-mysql \
-e MYSQL_ROOT_PASSWORD=testpass \
-e MYSQL_DATABASE=test_db \
-p 3306:3306 \
mysql:8
# Redis
docker run -d --name test-redis \
-p 6379:6379 \
redis:7
# MongoDB
docker run -d --name test-mongo \
-e MONGO_INITDB_ROOT_USERNAME=admin \
-e MONGO_INITDB_ROOT_PASSWORD=testpass \
-p 27017:27017 \
mongo:7
Service Containers
# Mailpit (email testing -- catches all outgoing emails)
docker run -d --name test-mail \
-p 1025:1025 \
-p 8025:8025 \
axllent/mailpit
# WireMock (API mocking)
docker run -d --name test-wiremock \
-p 8080:8080 \
wiremock/wiremock:latest
# Selenium Grid (browser testing)
docker run -d --name selenium-hub \
-p 4442-4444:4442-4444 \
selenium/hub:latest
Docker Compose for Test Environments
Docker Compose lets you define multi-container environments in a YAML file. This is the standard way to manage test environments that require multiple services.
Example: docker-compose.test.yml
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
NODE_ENV: test
DATABASE_URL: postgres://postgres:testpass@db:5432/test_db
REDIS_URL: redis://cache:6379
MAIL_HOST: mail
MAIL_PORT: 1025
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: testpass
POSTGRES_DB: test_db
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
cache:
image: redis:7
ports:
- "6379:6379"
mail:
image: axllent/mailpit
ports:
- "1025:1025"
- "8025:8025"
Compose Commands
# Start all services
docker compose -f docker-compose.test.yml up -d
# View logs from all services
docker compose -f docker-compose.test.yml logs -f
# View logs from a specific service
docker compose -f docker-compose.test.yml logs -f app
# Run tests against the environment
npm run test:integration
# Stop and remove everything (including volumes)
docker compose -f docker-compose.test.yml down -v
# Rebuild after code changes
docker compose -f docker-compose.test.yml up -d --build
Full Test Cycle with Compose
# One-command test cycle
docker compose -f docker-compose.test.yml up -d && \
npm run test:integration ; \
docker compose -f docker-compose.test.yml down -v
The semicolon (;) before down ensures cleanup happens even if tests fail.
Docker Troubleshooting for QA
Common Issues
| Issue | Diagnosis | Fix |
|---|---|---|
| Container won't start | docker logs <container> |
Check error messages in logs |
| Port already in use | lsof -i :<port> |
Stop conflicting process or use different port |
| Container runs but app doesn't connect | docker network inspect |
Ensure containers are on the same network |
| "No space left on device" | docker system df |
Run docker system prune to clean up |
| Tests fail with connection refused | Check depends_on and healthchecks |
Add healthcheck; wait for service readiness |
Cleanup Commands
# Show disk usage by Docker
docker system df
# Remove all unused data (stopped containers, dangling images, unused networks)
docker system prune
# Remove all unused data including unused images (more aggressive)
docker system prune -a
# Remove specific types
docker container prune # Stopped containers
docker image prune # Dangling images
docker volume prune # Unused volumes
docker network prune # Unused networks
Docker in CI/CD
Most CI platforms support Docker natively. GitHub Actions uses the services keyword to run Docker containers alongside your tests:
services:
postgres:
image: postgres:16
env:
POSTGRES_PASSWORD: testpass
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
This is equivalent to running docker run with the same parameters, but managed by the CI platform.
Hands-On Exercise
- Run a PostgreSQL container and connect to it with
psql(viadocker exec) - Create a
docker-compose.test.ymlwith an app, database, and Redis - Start the environment, run tests, and tear it down
- Intentionally break a service and use
docker logsto diagnose the issue - Check Docker disk usage on your machine and clean up unused resources
- Add a healthcheck to your database service and verify it works with
docker compose ps