Integration Testing PHP with Docker: A Complete Guide

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Integration Testing PHP with Docker: From A to Z

\n\n

Modern PHP development is unthinkable without quality testing. While unit tests check isolated pieces of code, integration testing ensures that all system components work together: the database, Redis, external APIs, and the file system. Docker makes this process reproducible, fast, and independent of the developer's environment. In this article, we will break down how to set up integration testing for a PHP application using Docker from scratch.

\n\n

Why Docker is the Ideal Tool for Integration Tests?

\n\n

Traditional problems of integration testing:

\n
    \n
  • Environment dependency — each developer has their own version of MySQL, PHP, Redis.
  • \n
  • Data pollution — tests leave garbage in the real database.
  • \n
  • Difficulty of parallel execution — tests conflict with each other.
  • \n
\n\n

Docker solves these problems:

\n
    \n
  • Each test runs in a clean container with known versions of dependencies.
  • \n
  • Containers are isolated and removed after the run.
  • \n
  • Docker Compose allows you to spin up the entire infrastructure with a single command.
  • \n
\n\n

Step 1: Project Structure and Docker Compose

\n\n

Let's start with a typical Symfony PHP application. Create a file docker-compose.test.yml for the test environment:

\n\n
version: '3.8'\nservices:\n  php:\n    build:\n      context: .\n      dockerfile: Dockerfile.test\n    environment:\n      DATABASE_URL: "mysql://test:test@mysql:3306/test_db?serverVersion=8.0"\n      REDIS_URL: "redis://redis:6379"\n    depends_on:\n      - mysql\n      - redis\n\n  mysql:\n    image: mysql:8.0\n    environment:\n      MYSQL_ROOT_PASSWORD: root\n      MYSQL_DATABASE: test_db\n      MYSQL_USER: test\n      MYSQL_PASSWORD: test\n    tmpfs: /var/lib/mysql  # Data in memory — faster\n\n  redis:\n    image: redis:7-alpine\n    tmpfs: /data
\n\n

File Dockerfile.test:

\n\n
FROM php:8.2-cli\n\nRUN docker-php-ext-install pdo_mysql\nRUN pecl install redis && docker-php-ext-enable redis\n\nCOPY --from=composer:latest /usr/bin/composer /usr/bin/composer\n\nWORKDIR /app\nCOPY . .\n\nRUN composer install --no-interaction --prefer-dist\n\nCMD ["vendor/bin/phpunit", "--configuration", "phpunit.xml.dist"]
\n\n

Note the tmpfs — this mounts a temporary file system in memory. The database and Redis will run many times faster than on disk, which is critical for fast tests.

\n\n

Step 2: Configuring PHPUnit for Integration Tests

\n\n

Let's create a separate configuration phpunit.integration.xml.dist:

\n\n
<?xml version="1.0" encoding="UTF-8"?>\n<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n         xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"\n         bootstrap="tests/bootstrap.php"\n         colors="true">\n    <testsuites>\n        <testsuite name="Integration">\n            <directory>tests/Integration</directory>\n        </testsuite>\n    </testsuites>\n    <php>\n        <env name="APP_ENV" value="test"/>\n        <env name="KERNEL_CLASS" value="App\\Kernel"/>\n    </php>\n</phpunit>
\n\n

File tests/bootstrap.php for initializing the test environment:

\n\n
<?php\n\nrequire dirname(__DIR__) . '/vendor/autoload.php';\n\n// Wait until MySQL and Redis are ready\n$maxAttempts = 30;\n$connected = false;\n\nfor ($i = 0; $i < $maxAttempts; $i++) {\n    try {\n        $pdo = new PDO(\n            $_ENV['DATABASE_URL'],\n            null, null,\n            [PDO::ATTR_TIMEOUT => 1]\n        );\n        $connected = true;\n        break;\n    } catch (PDOException $e) {\n        sleep(1);\n    }\n}\n\                

Blogs

Book Recommendations