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

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 - php


Key points:

  • The test_runner service 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 3307 to 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-dist


This 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

Blogs

Book Recommendations