QA Engineer Skills 2026QA-2026Docker Basics for QA

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

  1. Run a PostgreSQL container and connect to it with psql (via docker exec)
  2. Create a docker-compose.test.yml with an app, database, and Redis
  3. Start the environment, run tests, and tear it down
  4. Intentionally break a service and use docker logs to diagnose the issue
  5. Check Docker disk usage on your machine and clean up unused resources
  6. Add a healthcheck to your database service and verify it works with docker compose ps