Lab 12: Testing with PHPUnit

Time: 40 minutes | Level: Advanced | Docker: docker run -it --rm php:8.3-cli bash

PHPUnit 11 embraces PHP 8 attributes for test configuration. This lab covers data providers, mocking, test doubles, code coverage, and integration testing with SQLite.


Step 1: Setting Up PHPUnit

docker run --rm php:8.3-cli sh -c "
cd /tmp &&
php -r \"copy('https://getcomposer.org/installer', 'cs.php');\" 2>/dev/null || \
  wget -q -O cs.php https://getcomposer.org/installer &&
php cs.php --quiet && mv composer.phar /usr/local/bin/composer &&
mkdir testapp && cd testapp &&
composer require --no-progress phpunit/phpunit:^11 2>&1 | tail -5 &&
echo '---' &&
vendor/bin/phpunit --version
"

📸 Verified Output:

Generating autoload files
PHPUnit 11.x.x by Sebastian Bergmann and contributors.

Step 2: Basic Test with #[DataProvider]

Run: vendor/bin/phpunit tests/CalculatorTest.php --testdox

📸 Verified Output:


Step 3: createStub vs createMock

💡 Stub = controls return values only. Mock = controls returns + verifies call expectations.


Step 4: getMockBuilder for Advanced Mocks


Step 5: Test Doubles — Spy Pattern

💡 Test Double Types: Dummy (unused placeholder) → Stub (returns fixed values) → Spy (records calls) → Mock (verifies expectations) → Fake (working simplified implementation).


Step 6: Code Coverage with PCOV


Step 7: Integration Test with SQLite PDO

Run: vendor/bin/phpunit tests/Integration/ --testdox

📸 Verified Output:


Step 8: Capstone — Full Test Suite

📸 Verified Output:


Summary

Feature
PHPUnit 11 Syntax
Notes

Data provider

#[DataProvider('methodName')]

Replaces @dataProvider annotation

Mark as test

#[Test] attribute

Alternative to test prefix

Create stub

$this->createStub(Interface::class)

Returns values, no expectations

Create mock

$this->createMock(Interface::class)

Returns + verifies calls

Expect exception

$this->expectException(Ex::class)

Before code that should throw

Expect message

$this->expectExceptionMessage('...')

Check exception message

Mock builder

$this->getMockBuilder(Cls::class)

Full control over mock creation

Consecutive returns

->willReturnOnConsecutiveCalls(...)

Different values per call

Integration test

new PDO('sqlite::memory:')

In-memory DB per test

setUp/tearDown

setUp(): void

Runs before/after each test

Last updated