Automation5 min read

Docker for Testers: Run Your Tests Anywhere

Containerize your test environments for consistent, portable, and scalable test execution.

“It works on my machine” - the classic developer excuse. But as testers, we face the same problem: tests pass locally but fail in CI. Different browser versions, missing dependencies, environment misconfigurations. Docker solves this by packaging everything your tests need into portable containers.

Why Docker for Testing?

Docker Basics for Testers

You don’t need to be a Docker expert. These concepts are enough to get started:

ConceptWhat It IsAnalogy
ImageTemplate with OS, tools, dependenciesRecipe
ContainerRunning instance of an imageCooked dish
DockerfileInstructions to build an imageRecipe steps
Docker ComposeDefine multi-container applicationsFull menu
VolumePersistent storage for containersExternal hard drive

Essential Docker Commands

# Pull an image from Docker Hub
docker pull selenium/standalone-chrome

# Run a container
docker run -d -p 4444:4444 selenium/standalone-chrome

# List running containers
docker ps

# Stop a container
docker stop <container-id>

# Remove a container
docker rm <container-id>

# View container logs
docker logs <container-id>

# Execute command inside container
docker exec -it <container-id> /bin/bash

Running Selenium in Docker

Quick Start: Standalone Chrome

# Run Selenium with Chrome
docker run -d -p 4444:4444 -p 7900:7900 --shm-size="2g" selenium/standalone-chrome:latest

# Access Selenium Grid console: http://localhost:4444
# Watch browser via VNC: http://localhost:7900 (password: secret)

Connect Your Tests

@BeforeEach
public void setup() throws MalformedURLException {
    ChromeOptions options = new ChromeOptions();

    // Connect to Docker Selenium
    driver = new RemoteWebDriver(
        new URL("http://localhost:4444/wd/hub"),
        options
    );
}

@Test
public void testInDocker() {
    driver.get("https://example.com");
    assertThat(driver.getTitle()).contains("Example");
}

Selenium Grid with Docker Compose

For parallel testing across multiple browsers, use Docker Compose:

# docker-compose.yml
version: "3"
services:
  selenium-hub:
    image: selenium/hub:latest
    container_name: selenium-hub
    ports:
      - "4442:4442"
      - "4443:4443"
      - "4444:4444"

  chrome:
    image: selenium/node-chrome:latest
    shm_size: 2gb
    depends_on:
      - selenium-hub
    environment:
      - SE_EVENT_BUS_HOST=selenium-hub
      - SE_EVENT_BUS_PUBLISH_PORT=4442
      - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
      - SE_NODE_MAX_SESSIONS=2

  firefox:
    image: selenium/node-firefox:latest
    shm_size: 2gb
    depends_on:
      - selenium-hub
    environment:
      - SE_EVENT_BUS_HOST=selenium-hub
      - SE_EVENT_BUS_PUBLISH_PORT=4442
      - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
      - SE_NODE_MAX_SESSIONS=2

  edge:
    image: selenium/node-edge:latest
    shm_size: 2gb
    depends_on:
      - selenium-hub
    environment:
      - SE_EVENT_BUS_HOST=selenium-hub
      - SE_EVENT_BUS_PUBLISH_PORT=4442
      - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
# Start the grid
docker-compose up -d

# Scale Chrome nodes for parallel testing
docker-compose up -d --scale chrome=5

# Stop and remove
docker-compose down

Containerizing Your Test Framework

Package your entire test suite as a Docker image:

Dockerfile for Java/Maven Tests

# Dockerfile
FROM maven:3.9-eclipse-temurin-17

# Set working directory
WORKDIR /tests

# Copy pom.xml first (for dependency caching)
COPY pom.xml .
RUN mvn dependency:resolve

# Copy test code
COPY src ./src
COPY testng.xml .

# Default command
CMD ["mvn", "test"]

Build and Run

# Build your test image
docker build -t my-test-suite:latest .

# Run tests against Docker Selenium
docker run --network host my-test-suite:latest

# Run with environment variables
docker run --network host \
  -e BASE_URL=https://staging.example.com \
  -e BROWSER=chrome \
  my-test-suite:latest

# Mount test results to host
docker run --network host \
  -v $(pwd)/reports:/tests/target/surefire-reports \
  my-test-suite:latest

Complete Test Environment

Combine your tests with Selenium Grid in one compose file:

# docker-compose.test.yml
version: "3"
services:
  selenium-hub:
    image: selenium/hub:latest
    ports:
      - "4444:4444"

  chrome:
    image: selenium/node-chrome:latest
    shm_size: 2gb
    depends_on:
      - selenium-hub
    environment:
      - SE_EVENT_BUS_HOST=selenium-hub
      - SE_EVENT_BUS_PUBLISH_PORT=4442
      - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
      - SE_NODE_MAX_SESSIONS=4

  test-runner:
    build: .
    depends_on:
      - chrome
    environment:
      - SELENIUM_HUB_URL=http://selenium-hub:4444/wd/hub
      - BASE_URL=https://example.com
    volumes:
      - ./reports:/tests/target/surefire-reports
    command: ["mvn", "test", "-Dselenium.hub=http://selenium-hub:4444/wd/hub"]
# Run complete test environment
docker-compose -f docker-compose.test.yml up --build --abort-on-container-exit

# View results in ./reports folder

CI/CD Integration

GitHub Actions

# .github/workflows/test.yml
name: Selenium Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest

    services:
      selenium:
        image: selenium/standalone-chrome:latest
        ports:
          - 4444:4444
        options: --shm-size=2g

    steps:
      - uses: actions/checkout@v4

      - name: Set up JDK 17
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'

      - name: Run Tests
        run: mvn test -Dselenium.hub=http://localhost:4444/wd/hub

      - name: Upload Test Results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: test-results
          path: target/surefire-reports/

Jenkins Pipeline

pipeline {
    agent any

    stages {
        stage('Start Selenium Grid') {
            steps {
                sh 'docker-compose up -d selenium-hub chrome'
                sh 'sleep 10' // Wait for grid to be ready
            }
        }

        stage('Run Tests') {
            steps {
                sh '''
                    docker run --network host \
                        -v ${WORKSPACE}/reports:/tests/target \
                        my-test-suite:latest \
                        mvn test -Dselenium.hub=http://localhost:4444/wd/hub
                '''
            }
        }

        stage('Publish Results') {
            steps {
                junit 'reports/surefire-reports/*.xml'
            }
        }
    }

    post {
        always {
            sh 'docker-compose down'
        }
    }
}

API Testing with Docker

Run API tests against dockerized services:

# docker-compose.api-test.yml
version: "3"
services:
  # Your application
  api:
    image: my-api:latest
    ports:
      - "8080:8080"
    environment:
      - DATABASE_URL=jdbc:postgresql://db:5432/testdb

  # Test database
  db:
    image: postgres:15
    environment:
      - POSTGRES_DB=testdb
      - POSTGRES_USER=test
      - POSTGRES_PASSWORD=test

  # API tests
  api-tests:
    build:
      context: ./api-tests
    depends_on:
      - api
    environment:
      - API_BASE_URL=http://api:8080
    command: ["./wait-for-it.sh", "api:8080", "--", "mvn", "test"]

Debugging in Docker

View Browser via VNC

# Use debug images for VNC access
docker run -d -p 4444:4444 -p 7900:7900 --shm-size="2g" \
    selenium/standalone-chrome:latest

# Open browser: http://localhost:7900
# Password: secret

Access Container Shell

# Get shell access to running container
docker exec -it <container-id> /bin/bash

# Check logs
docker logs -f <container-id>

# Inspect container details
docker inspect <container-id>

Best Practices

Example .dockerignore

# .dockerignore
target/
.git/
.idea/
*.log
node_modules/
reports/
screenshots/

Health Check Example

# Wait for Selenium to be ready
services:
  chrome:
    image: selenium/standalone-chrome:latest
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:4444/wd/hub/status"]
      interval: 10s
      timeout: 5s
      retries: 5

  tests:
    depends_on:
      chrome:
        condition: service_healthy

Common Issues and Solutions

IssueSolution
Chrome crashesAdd --shm-size="2g"
Connection refusedWait for container to be ready, check ports
Tests can’t reach appUse Docker network, not localhost
Slow buildsCache dependencies, use multi-stage builds
Out of disk spaceRun docker system prune regularly

Quick Reference

# Selenium standalone browsers
docker run -d -p 4444:4444 --shm-size=2g selenium/standalone-chrome
docker run -d -p 4444:4444 --shm-size=2g selenium/standalone-firefox
docker run -d -p 4444:4444 --shm-size=2g selenium/standalone-edge

# With VNC access (port 7900, password: secret)
docker run -d -p 4444:4444 -p 7900:7900 --shm-size=2g selenium/standalone-chrome

# Docker Compose commands
docker-compose up -d          # Start in background
docker-compose down           # Stop and remove
docker-compose logs -f        # Follow logs
docker-compose ps             # List containers
docker-compose up --scale chrome=5  # Scale nodes

# Cleanup
docker system prune           # Remove unused data
docker image prune            # Remove unused images
docker container prune        # Remove stopped containers

Conclusion

Docker transforms test automation from “works on my machine” to “works everywhere.” Start with running Selenium in Docker, then gradually containerize your entire test infrastructure. The initial setup takes time, but the consistency and reliability you gain are worth it.

Remember: Docker is a tool, not a goal. Use it to solve real problems - environment inconsistency, parallel execution, CI/CD integration. Don’t containerize just for the sake of containerizing.