Introduction: Why Integration Testing PHP in Docker is a Must-Have
Modern PHP application development is rarely limited to a single server. Databases, caches (Redis, Memcached), queues (RabbitMQ), external APIs — all of these require checking component interaction. Unit tests isolate code but do not guarantee that class A works correctly with class B through a real database. This is where integration testing comes to the rescue.
The problem with the classic approach is environment setup. A developer spends hours setting up MySQL, configuring permissions, and installing PHP extensions. Docker solves this problem once and for all. In this article, we will explore how to organize integration testing for a PHP application using Docker and Docker Compose. You will get ready-made configs, code examples, and optimization recommendations.
1. Setting Up Docker Environment for PHP Tests
1.1. Basic Project Structure
Let's assume we have a simple PHP application on Symfony or Laravel (any framework will work). Create a docker-compose.yml file in the project root:
version: '3.8'
services: php: build: . volumes: - .:/var/www/html environment: - DATABASE_URL=mysql://test_user:test_pass@db:3306/test_db depends_on: - db
db: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: root_pass MYSQL_DATABASE: test_db MYSQL_USER: test_user MYSQL_PASSWORD: test_pass ports: - "3307:3306" # Forward to a non-standard port to avoid conflicts with local DB
test_runner: image: php:8.2-cli volumes: - .:/var/www/html working_dir: /var/www/html entrypoint: ["vendor/bin/phpunit"] depends_on: - db - phpKey points:
- The
test_runnerservice uses a separate container with PHP CLI — this isolates test execution from the main application. - The database is started with a clean schema. For each test run, we can recreate it.
- The MySQL port is forwarded to
3307to avoid interfering with the developer's local database.
1.2. Dockerfile for the PHP Application
Create a Dockerfile for the main php service:
FROM php:8.2-fpm
RUN docker-php-ext-install pdo_mysql mysqli
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
WORKDIR /var/www/html
COPY . .
RUN composer install --no-interaction --prefer-distThis image will be used both for development and for tests. It is important to install the pdo_mysql extension — without it, tests working with the database will fail.
2. Writing Integration Tests with PHPUnit
2.1. Basic PHPUnit Configuration
Make sure you have PHPUnit installed (via Composer). The phpunit.xml file should point to the test database:
<?xml version="1.0" encoding="UTF-8"?><phpunit bootstrap="vendor/autoload.php"> <php> <env name="DATABASE_URL" value="mysql://test_user:test_pass@db:3306/test_db" /> <env name="APP_ENV" value="test" /> </php> <testsuites> <testsuite name="Integration"> <directory>tests/Integration</directory> </testsuite> </testsuites></phpunit>2.2. Example of an Integration Test: Working with the Database
Let's say we have a user repository. Let's write a test that checks saving and searching:
<?php
namespace App\\Tests\\Integration;
use App\\Entity\\User;use App\\Repository\\UserRepository;use Doctrine\\ORM\\EntityManagerInterface;use Symfony\\Bundle\\FrameworkBundle\\Test\\KernelTestCase;
class UserRepos