Using Containers in Tests¶
This guide shows how to integrate Solti-Containers services into your testing workflows.
Testing Philosophy¶
The containers are designed for ephemeral testing environments:
- Fast Setup: Containers start in seconds
- Isolation: Each test run can use fresh containers
- Cleanup: Easy removal after tests complete
- Reproducibility: Same environment every time
Integration Patterns¶
Pattern 1: Manual Lifecycle¶
For interactive testing and development:
# Start services before testing
./manage-svc.sh redis deploy
./manage-svc.sh mattermost deploy
# Run your tests
pytest tests/integration/
# Clean up after
./manage-svc.sh redis remove
./manage-svc.sh mattermost remove
Pattern 2: Test Wrapper Script¶
Create a test wrapper that manages container lifecycle:
#!/bin/bash
# test-with-containers.sh
# Deploy required services
echo "Deploying test services..."
./manage-svc.sh redis deploy
./manage-svc.sh elasticsearch deploy
# Wait for services to be ready
./svc-exec.sh redis verify
./svc-exec.sh elasticsearch verify
# Run tests
echo "Running tests..."
pytest "$@"
TEST_EXIT=$?
# Cleanup
echo "Cleaning up..."
./manage-svc.sh redis remove
./manage-svc.sh elasticsearch remove
exit $TEST_EXIT
Usage:
Pattern 3: Makefile Integration¶
.PHONY: test-integration test-setup test-cleanup
test-setup:
./manage-svc.sh redis deploy
./manage-svc.sh mattermost deploy
./svc-exec.sh redis verify
./svc-exec.sh mattermost verify
test-integration: test-setup
pytest tests/integration/ || (make test-cleanup && exit 1)
make test-cleanup
test-cleanup:
./manage-svc.sh redis remove
./manage-svc.sh mattermost remove
Usage:
Pattern 4: Python Test Fixtures¶
Using pytest fixtures for container management:
# conftest.py
import pytest
import subprocess
import time
@pytest.fixture(scope="session")
def redis_container():
"""Deploy Redis container for testing session."""
# Deploy
subprocess.run(["./manage-svc.sh", "redis", "deploy"], check=True)
time.sleep(2) # Wait for startup
# Verify
subprocess.run(["./svc-exec.sh", "redis", "verify"], check=True)
yield "localhost:6379"
# Cleanup
subprocess.run(["./manage-svc.sh", "redis", "remove"], check=True)
@pytest.fixture(scope="session")
def mattermost_container():
"""Deploy Mattermost container for testing session."""
subprocess.run(["./manage-svc.sh", "mattermost", "deploy"], check=True)
time.sleep(30) # Mattermost takes longer to start
subprocess.run(["./svc-exec.sh", "mattermost", "verify"], check=True)
yield "http://localhost:8065"
subprocess.run(["./manage-svc.sh", "mattermost", "remove"], check=True)
# Test usage
def test_redis_caching(redis_container):
import redis
r = redis.Redis.from_url(f"redis://{redis_container}")
r.set("test", "value")
assert r.get("test") == b"value"
def test_mattermost_webhook(mattermost_container):
import requests
# Setup webhook and test
pass
CI/CD Integration¶
GitHub Actions¶
name: Integration Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Podman
run: |
sudo apt-get update
sudo apt-get install -y podman
- name: Install Ansible
run: pip install ansible
- name: Deploy test services
run: |
./manage-svc.sh redis deploy
./manage-svc.sh mattermost deploy
- name: Verify services
run: |
./svc-exec.sh redis verify
./svc-exec.sh mattermost verify
- name: Run tests
run: pytest tests/integration/
- name: Cleanup
if: always()
run: |
./manage-svc.sh redis remove
./manage-svc.sh mattermost remove
GitLab CI¶
integration-test:
image: debian:bookworm
before_script:
- apt-get update
- apt-get install -y podman ansible python3-pip
- pip3 install pytest
script:
- ./manage-svc.sh redis deploy
- ./manage-svc.sh elasticsearch deploy
- ./svc-exec.sh redis verify
- ./svc-exec.sh elasticsearch verify
- pytest tests/integration/
after_script:
- ./manage-svc.sh redis remove || true
- ./manage-svc.sh elasticsearch remove || true
Molecule Testing¶
For testing Ansible roles with containers:
# molecule/default/molecule.yml
---
dependency:
name: galaxy
driver:
name: podman
platforms:
- name: test-instance
image: debian:bookworm
pre_build_image: true
provisioner:
name: ansible
inventory:
group_vars:
all:
redis_state: present
mattermost_state: present
verifier:
name: ansible
# molecule/default/verify.yml
---
- name: Verify
hosts: all
tasks:
- name: Verify Redis is accessible
command: redis-cli -h localhost ping
register: redis_ping
failed_when: redis_ping.stdout != "PONG"
- name: Verify Mattermost is accessible
uri:
url: http://localhost:8065
status_code: 200
Service Dependencies¶
When tests require multiple services:
# conftest.py
@pytest.fixture(scope="session")
def test_environment():
"""Deploy all required services."""
services = ["redis", "mattermost", "elasticsearch"]
# Deploy all
for service in services:
subprocess.run(["./manage-svc.sh", service, "deploy"], check=True)
# Wait for all to be ready
time.sleep(10)
# Verify all
for service in services:
subprocess.run(["./svc-exec.sh", service, "verify"], check=True)
yield {
"redis": "localhost:6379",
"mattermost": "http://localhost:8065",
"elasticsearch": "http://localhost:9200"
}
# Cleanup all
for service in services:
subprocess.run(["./manage-svc.sh", service, "remove"], check=True)
Best Practices¶
1. Use Session Fixtures¶
Deploy containers once per test session, not per test:
2. Verify Before Testing¶
Always verify services are ready before running tests:
3. Handle Cleanup¶
Use try/finally or if: always() in CI to ensure cleanup:
4. Parallel Testing¶
For parallel test execution, use unique port assignments:
# test-env-1
redis_port: 6379
mattermost_port: 8065
# test-env-2
redis_port: 6380
mattermost_port: 8066
5. Data Isolation¶
Always use remove between test runs to ensure clean state:
./manage-svc.sh redis remove # Keeps data
./manage-svc.sh redis remove -e redis_delete_data=true # Deletes data
Common Patterns by Service¶
Redis¶
- Cache testing
- Session storage tests
- Queue processing tests
- Rate limiting tests
Mattermost¶
- Webhook delivery tests
- Notification integration tests
- Bot interaction tests
Elasticsearch¶
- Search functionality tests
- Log ingestion tests
- Analytics pipeline tests
HashiVault¶
- Secret retrieval tests
- Dynamic credential tests
- Certificate generation tests
MinIO¶
- S3 API compatibility tests
- Backup workflow tests
- Object storage tests
Troubleshooting¶
Services Not Starting¶
# Check status
systemctl --user status <service>-pod
# View logs
podman logs <service>-svc
# Retry deployment
./manage-svc.sh <service> remove
./manage-svc.sh <service> deploy
Port Conflicts¶
# Check what's using a port
sudo lsof -i :6379
# Use different port
./manage-svc.sh redis deploy -e redis_port=6380
Slow Startup¶
Some services (Elasticsearch, Mattermost) take longer to start:
Or check health endpoints:
import time
import requests
def wait_for_service(url, timeout=60):
start = time.time()
while time.time() - start < timeout:
try:
response = requests.get(url)
if response.status_code == 200:
return True
except requests.exceptions.ConnectionError:
pass
time.sleep(2)
return False
wait_for_service("http://localhost:8065") # Mattermost