Integration Testing PHP with Docker: Advanced Techniques

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: Advanced Level

Integration testing is a critically important stage of development, especially for complex PHP applications that interact with databases, queues, external APIs, and other services. Docker allows you to spin up an isolated environment, as close to production as possible, and run tests in reproducible conditions. In this article, we will cover advanced techniques: organizing test containers, simulating external dependencies, parallel execution, and integration with CI/CD.



1. Test Environment Architecture with docker-compose

For integration tests, we use a separate docker-compose.test.yml file that starts only the necessary services: PHP-FPM with test code, a database (e.g., PostgreSQL), Redis, and optionally a mock server for external APIs.

version: '3.8'services:  php-test:    build:      context: .      dockerfile: Dockerfile.test    volumes:      - .:/var/www/html    environment:      DATABASE_URL: "pgsql://test:test@db-test:5432/testdb"      REDIS_URL: "redis://redis-test:6379"      EXTERNAL_API_URL: "http://mock-server:1080"    depends_on:      - db-test      - redis-test      - mock-server

db-test: image: postgres:15-alpine environment: POSTGRES_DB: testdb POSTGRES_USER: test POSTGRES_PASSWORD: test ports: - "5433:5432" # Avoid conflict with the main database

redis-test: image: redis:7-alpine ports: - "6380:6379"

mock-server: image: mockserver/mockserver:latest environment: MOCKSERVER_PROPERTY_FILE: /config/mockserver.properties volumes: - ./tests/mockserver:/config ports: - "1080:1080"

Note: we use separate ports for test services to avoid interfering with local development.



2. Preparing PHPUnit and Initializing the Test Database

Configuring phpunit.xml for integration tests:

<phpunit bootstrap="tests/bootstrap.php">    <testsuites>        <testsuite name="Integration">            <directory>tests/Integration</directory>        </testsuite>    </testsuites>    <php>        <env name="APP_ENV" value="test"/>        <env name="KERNEL_CLASS" value="App\\Kernel"/>    </php></phpunit>

The tests/bootstrap.php file is responsible for initializing the database schema and populating it with test data:

require __DIR__ . '/../vendor/autoload.php';

// Create the database schema$pdo = new PDO('pgsql:host=db-test;port=5432;dbname=testdb', 'test', 'test');$pdo->exec(file_get_contents(__DIR__ . '/../migrations/schema.sql'));

// Load fixturesrequire __DIR__ . '/Fixtures/LoadFixtures.php';

This approach guarantees that each test run starts from a clean, predictable state.



3. Testing with External APIs: MockServer and WireMock

To simulate external HTTP services, we use MockServer (or WireMock). Create an expectations configuration in a JSON file:

// tests/mockserver/expectations/payment.json{  "httpRequest": {    "method": "POST",    "path": "/api/v1/charge"  },  "httpResponse": {    "statusCode": 200,    "body": "{\\"status\\": \\"success\\", \\"transaction_id\\": \\"txn_123\\"}",    "headers": {      "Content-Type": "application/json"    }  }}

In the PHP test code, we send a request to the real URL, which points to the mock server via an environment variable:

class PaymentIntegrationTest extends TestCase{    private PaymentService $paymentService;

protected function setUp(): void { $this->paymentService = new PaymentService( new HttpClient(['base_uri' => $_ENV['EXTERNAL_API_URL']])

Blogs

Book Recommendations