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:
| Concept | What It Is | Analogy |
|---|---|---|
| Image | Template with OS, tools, dependencies | Recipe |
| Container | Running instance of an image | Cooked dish |
| Dockerfile | Instructions to build an image | Recipe steps |
| Docker Compose | Define multi-container applications | Full menu |
| Volume | Persistent storage for containers | External 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
| Issue | Solution |
|---|---|
| Chrome crashes | Add --shm-size="2g" |
| Connection refused | Wait for container to be ready, check ports |
| Tests can’t reach app | Use Docker network, not localhost |
| Slow builds | Cache dependencies, use multi-stage builds |
| Out of disk space | Run 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.