Integration Testing PHP with Docker: From Basic Scenarios to Industrial Solutions
\n\nIntegration testing is a critically important development stage that verifies the interaction between various system components: the database, cache, external APIs, and the business logic itself. In the PHP world, where applications often work in conjunction with MySQL/PostgreSQL, Redis, and web servers, Docker becomes an indispensable tool for creating isolated, reproducible test environments. In this article, we will explore advanced techniques for integration testing PHP applications using Docker that will help you take code quality to the next level.
\n\nWhy Docker is the Ideal Companion for PHP Integration Tests
\n\nTraditional approaches to integration testing often suffer from environment issues: databases may differ on the local machine and CI server, ports may be occupied, and dependencies may not match. Docker solves these problems radically:
\n\n- \n
- Isolation: each test scenario runs in a clean container, eliminating side effects from previous tests. \n
- Reproducibility: images with fixed versions of PHP, extensions, and services guarantee that tests will run identically on any machine. \n
- Parallelism: you can run multiple test suites simultaneously, each in its own container with a unique database. \n
- Speed: modern tools such as Testcontainers or Docker Compose allow you to spin up and tear down environments in seconds. \n
This is especially relevant for PHP, as a typical application interacts with many external resources — from queues (RabbitMQ) to search engines (Elasticsearch).
\n\nSetting Up a Multi-Level Test Environment with Docker Compose
\n\nThe first step towards advanced testing is creating a flexible docker-compose.yml that includes not only PHP but also all necessary services. Let's consider an example for a Laravel/Symfony application that uses MySQL, Redis, and an external REST API:
version: '3.8'\n\nservices:\n php-test:\n image: php:8.2-cli\n volumes:\n - ./:/app\n working_dir: /app\n environment:\n - DB_HOST=mysql-test\n - DB_DATABASE=test_db\n - REDIS_HOST=redis-test\n - API_BASE_URL=http://mock-api:8080\n depends_on:\n mysql-test:\n condition: service_healthy\n redis-test:\n condition: service_started\n mock-api:\n condition: service_started\n\n mysql-test:\n image: mysql:8.0\n environment:\n MYSQL_ROOT_PASSWORD: root\n MYSQL_DATABASE: test_db\n healthcheck:\n test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]\n interval: 5s\n timeout: 3s\n retries: 5\n\n redis-test:\n image: redis:7-alpine\n\n mock-api:\n image: wiremock/wiremock:latest\n volumes:\n - ./tests/mock-api:/home/wiremock\n\n\nThe key point is the use of WireMock to simulate external APIs. This allows you to test integration with services that may be unavailable or unstable in the test environment. WireMock runs in a separate container and returns predefined responses based on JSON configuration files.
\n\nTo run tests inside this environment, use the script:
\n\n#!/bin/bash\ndocker-compose -f docker-compose.test.yml up -d\ndocker-compose -f docker-compose.test.yml exec php-test vendor/bin/phpunit --testsuite=Integration\ndocker-compose -f docker-compose.test.yml down\n\nAdvanced Techniques: Dynamic Container Creation and Parallel Execution
\n\nWhen a project grows, a static docker-compose.yml can become a bottleneck. This is where the Testcontainers library comes to the rescue (available for PHP via the testc