ENE is a powerful Docker-based end-to-end testing framework that spins up complete test environments with databases, services, and mocked APIs to validate your applications through comprehensive integration tests.
- π³ Docker-Native: Automatically manages containers for services, databases, and mocks
- π§ Multiple Service Types: HTTP servers, MongoDB, PostgreSQL, MinIO, HTTP mocks
- ποΈ Database Testing: First-class support for PostgreSQL and MongoDB query testing
- π Simple YAML Configuration: Declarative test definitions with fixtures and assertions
- π Variable Interpolation: Reuse values across tests with fixtures and service variables
- π Rich Assertions: JSON path queries, SQL/NoSQL result validation, header checks, MinIO state verification
- π― Test Isolation: Each suite runs in its own Docker network
- π Detailed Reports: HTML and JSON output formats
- β‘ Parallel Execution: Run multiple test suites concurrently
- π οΈ Easy Scaffolding: Generate new test suites with templates
- Docker (with daemon running)
- Docker Compose (for orchestration)
- Go 1.25+ (to build from source)
If using Colima on macOS, set these environment variables:
export TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE=/var/run/docker.sock
export DOCKER_HOST="unix://${HOME}/.colima/docker.sock"# Clone and build
git clone https://github.com/exapsy/ene
cd ene
go build -o ene .
# Optional: Install globally
sudo mv ene /usr/local/bin/
# Verify installation
ene version# Create a new test suite
ene scaffold-test my-first-test --tmpl=httpmock
# This creates: ./tests/my-first-test/suite.ymlEdit tests/my-first-test/suite.yml:
kind: e2e_test:v1
name: my-first-test
fixtures:
- api_key: test-key-123
units:
- name: api
kind: httpmock
app_port: 8080
routes:
- path: /health
method: GET
response:
status: 200
body:
status: ok
target: api
tests:
- name: health check
kind: http
request:
path: /health
method: GET
headers:
Authorization: Bearer {{ api_key }}
expect:
status_code: 200
body_asserts:
status: okRun your test:
# Validate configuration
ene dry-run
# Run the test
ene
# Run with verbose output
ene --verbose
# Run with HTML report
ene --html=report.htmlENE uses flexible test suite discovery and supports multiple directory structures:
your-project/
βββ tests/ # Test suites directory
βββ suite-name-1/
β βββ suite.yml # Test configuration (required)
β βββ Dockerfile # Optional service dockerfile
β βββ .env # Optional environment variables
β βββ db.js # Optional MongoDB migrations
β βββ migrations/ # Optional PostgreSQL migrations
βββ suite-name-2/
βββ suite.yml
ENE intelligently discovers test suites based on the path you provide:
Run all tests from project root:
cd your-project
ene # Discovers tests/ automatically (if it exists)Run a specific suite:
ene tests/suite-name-1 # By directory
ene tests/suite-name-1/suite.yml # By fileRun from any directory:
cd tests/suite-name-1
ene # Runs the suite in current directory
cd /anywhere
ene /path/to/tests/suite-name-1Nested structures work too:
tests/
βββ integration/
β βββ auth/suite.yml
β βββ payments/suite.yml
βββ unit/
βββ api/suite.yml
# Run all integration tests
ene tests/integration
# Run just auth tests
ene tests/integration/authπ Learn more: See Test Discovery Documentation for detailed discovery rules and examples.
# Run all tests
ene
# Run with verbose output
ene --verbose
# Run specific suite by path
ene tests/my-test
ene tests/integration/auth
# Run specific suite(s) by filtering
ene --suite=my-test
ene --suite=test1,test2
# Run tests matching pattern
ene --suite=user_,_api
# Run in parallel
ene --parallel
# Validate without running
ene dry-run
ene dry-run tests/my-test
# Generate reports
ene --html=report.html --json=report.json
# List all test suites
ene list-suites
ene list-suites tests/integration
# Create new test suite
ene scaffold-test my-new-test
ene scaffold-test api-test --tmpl=postgres,http
# Enable debug mode
ene --debug --verbose
# Cleanup old Docker images
ene --cleanup-cache
# Cleanup orphaned Docker resources
ene cleanup --dry-run # Preview what would be removed
ene cleanup --force # Remove orphaned resources
ene cleanup --older-than=1h # Remove resources older than 1 hourkind: e2e_test:v1 # Required: Version identifier
name: my-test-suite # Required: Suite name
# Optional: Reusable values
fixtures:
- user_id: 12345 # Simple value
- enabled: true # Boolean
- api_key: test-key-xyz # String
- test_data: # File-based fixture
file: ./data/user.json
# Required: Service containers
units:
- name: postgres
kind: postgres
image: postgres:14
app_port: 5432
database: testdb
user: testuser
password: testpass
migrations: ./migrations
- name: app
kind: http
dockerfile: ./Dockerfile
app_port: 8080
healthcheck: /health
build_timeout: 45s
startup_timeout: 30s
env:
- DATABASE_URL={{ postgres.dsn }}
- API_KEY={{ api_key }}
# Required: Default target service
target: app
# Required: Test cases
tests:
- name: create user
kind: http
request:
method: POST
path: /api/users
headers:
Content-Type: application/json
body:
name: John Doe
email: john@example.com
expect:
status_code: 201
body_asserts:
id:
present: true
name: John Doe- name: app
kind: http
dockerfile: Dockerfile # OR image: myapp:latest
app_port: 8080
healthcheck: /health # Optional health endpoint
build_timeout: 45s # Time allowed for building Docker image (default: 45s)
startup_timeout: 30s # Time allowed for service to become healthy (default: 30s)
env:
- KEY=value
cmd: # Optional command override
- ./app
- --port=8080Timeouts Explained:
build_timeout: Maximum time for Docker image build (downloading dependencies, compilation)startup_timeout: Maximum time for container startup and health check after build completes
- name: mock-api
kind: httpmock
app_port: 8080
routes:
- path: /api/users
method: GET
response:
status: 200
delay: 100ms # Optional delay
body:
users: []
headers:
Content-Type: application/json- name: postgres
kind: postgres
image: postgres:14
app_port: 5432
database: testdb
user: testuser
password: testpass
migrations: ./migrations # Directory with .sql files
startup_timeout: 30s- name: mongodb
kind: mongo
image: mongo:6.0
app_port: 27017
database: testdb
user: testuser
password: testpass
migrations: db.js # JavaScript migration file
startup_timeout: 30s- name: storage
kind: minio
image: minio/minio:latest
access_key: testkey
secret_key: testsecret
app_port: 9000
console_port: 9001
buckets:
- uploads
- processedSimple Values (Primitives):
fixtures:
- api_key: test-key-123
- user_id: 5432
- enabled: true
- description: |
Multi-line
text valueFile-Based Fixtures:
fixtures:
- test_payload:
file: ./testdata/payload.json
- large_data:
file: ./testdata/users.jsonUsage in Tests:
tests:
- name: test
kind: http
request:
path: /users/{{ user_id }}
headers:
Authorization: Bearer {{ api_key }}
body: "{{ test_payload }}"Access service connection details:
# PostgreSQL
{{ postgres.dsn }} # postgresql://user:pass@host:port/db
{{ postgres.host }}
{{ postgres.port }}
{{ postgres.database }}
# MongoDB
{{ mongodb.dsn }} # mongodb://user:pass@host:port/db
{{ mongodb.host }}
{{ mongodb.port }}
{{ mongodb.database }}
# MinIO
{{ storage.endpoint }} # External endpoint
{{ storage.local_endpoint }} # Internal Docker endpoint
{{ storage.access_key }}
{{ storage.secret_key }}
# HTTP Service
{{ app.host }}
{{ app.port }}
{{ app.endpoint }} # http://app:8080ENE supports multiple test types for different testing scenarios:
Test HTTP APIs with full request/response validation:
tests:
- name: create user
kind: http
request:
method: POST
path: /api/users
headers:
Content-Type: application/json
body:
name: John Doe
email: john@example.com
expect:
status_code: 201
body_asserts:
id:
present: true
name: John DoeExecute SQL queries and validate results:
tests:
- name: verify user count
kind: postgres
query: "SELECT COUNT(*) as count FROM users WHERE status = 'active'"
expect:
row_count: 1
column_values:
count: 5
- name: check user data
kind: postgres
query: "SELECT id, name, email FROM users WHERE id = 1"
expect:
rows:
- id: 1
name: "Alice"
email: "alice@example.com"π See POSTGRES_TESTS.md for complete PostgreSQL testing guide
Query MongoDB collections with find operations or aggregation pipelines:
tests:
- name: check active users
kind: mongo
collection: users
filter:
status: active
expect:
document_count: 5
- name: aggregate by role
kind: mongo
collection: users
pipeline:
- $group:
_id: "$role"
count: { $sum: 1 }
expect:
min_document_count: 2π See MONGO_QUICK_REFERENCE.md for complete MongoDB testing guide
Verify object storage state:
tests:
- name: verify upload
kind: minio
verify_state:
files_exist:
- uploads/file1.txt
bucket_counts:
uploads: 2
required:
buckets:
uploads:
- path: file1.txt
min_size: 10B
max_size: 10MBBody Assertions (JSON Path):
body_asserts:
# Simple equality (shorthand)
status: ok
# Detailed assertions
user.id:
present: true # Key exists
type: string # Type check
user.age:
">": 18 # Numeric comparison
"<": 100
items:
length: 5 # Array length
type: array
# Array containment
products:
contains_where:
name: iPhone
price:
">": 900
# All items must match
users:
all_match:
active: true
# No items should match
errors:
none_match:
critical: trueHeader Assertions:
header_asserts:
Content-Type: application/json # Simple equality
X-Request-ID:
present: true
matches: "^[0-9a-f-]{36}$"
Cache-Control:
contains: no-cacheMinIO State Verification:
- name: verify upload
kind: minio
verify_state:
files_exist:
- uploads/file1.txt
bucket_counts:
uploads: 2
required:
buckets:
uploads:
- path: file1.txt
min_size: 10B
max_size: 10MB
content_type: text/plain
max_age: 5m
forbidden:
buckets:
uploads:
- "*.tmp"ENE automatically manages Docker resources (containers, networks) during test execution. However, if tests are interrupted or fail, resources may be left behind.
ENE uses a CleanupRegistry that ensures proper cleanup order:
- Containers are removed first
- Networks are removed after containers detach
- Resources are cleaned even if individual cleanups fail
This prevents common errors like "network has active endpoints."
Use the ene cleanup command to remove orphaned resources:
# Interactive cleanup (shows what will be removed)
ene cleanup
# Preview without removing (dry-run)
ene cleanup --dry-run --verbose
# Force cleanup without confirmation
ene cleanup --force
# Clean specific resource types
ene cleanup networks # Networks only
ene cleanup containers # Containers only
# Age-based filtering
ene cleanup --older-than=1h # Resources older than 1 hour
ene cleanup --older-than=24h # Resources older than 1 day
# Include all resources (not just orphaned)
ene cleanup --all --force
# Verbose output for debugging
ene cleanup --verboseFor CI/CD:
# GitLab CI
after_script:
- ene cleanup --older-than=30m --force
# GitHub Actions
- name: Cleanup
if: always()
run: ene cleanup --older-than=30m --forceFor Local Development:
# Check for orphaned resources
ene cleanup --dry-run --verbose
# Clean up after testing
ene cleanup --forcePeriodic Cleanup (Cron):
# Add to crontab for nightly cleanup
0 2 * * * /usr/local/bin/ene cleanup --older-than=24h --forceIf you see orphaned Docker resources:
# 1. Discover what's orphaned
ene cleanup --dry-run --verbose
# 2. Check Docker resources manually
docker network ls | grep testcontainers
docker ps -a | grep testcontainers
# 3. Clean them up
ene cleanup --force
# 4. For stubborn resources, inspect and remove manually
docker network inspect <network-id>
docker rm -f <container-id>
docker network rm <network-id>For more details, see:
ene --verbose --debugdocker ps -a
docker logs <container-id>ene dry-run --verbosePort Already in Use:
- Change
app_portin your suite.yml - Check for conflicting services:
lsof -i :8080
Startup Timeout:
- Increase
startup_timeoutfor the unit - Check service logs for errors
Test Failures:
- Use
--verboseto see detailed request/response - Check assertion paths match your JSON structure
- Verify fixtures are interpolated correctly
- Quick Reference - Cheat sheet for common patterns
- CLI Usage - Complete command-line reference
- Configuration Reference - Full YAML schema documentation
- Examples - Real-world test examples
- Cleanup Architecture - Resource management internals
- Migration Guide - Upgrade to new cleanup system
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Write tests for new features
- Submit a pull request
- Issues: GitHub Issues
- Documentation: docs/
- WebSocket testing support
- gRPC testing support
- Redis unit type
- Kafka unit type
- Test retry strategies
- Performance benchmarking
- Visual test reports
Made with β€οΈ for better end-to-end testing
