Testing in PHP: Frameworks and Tools
Code quality is the foundation of a successful web project. For PHP developers, testing is no longer an option but a mandatory stage of development. In this article, we'll break down three key tools: PHPUnit (the industry standard), Pest (modern minimalism), and Codeception (integration testing). You'll learn how to write unit tests, work with mock objects, and implement TDD in PHP projects.
1. PHPUnit — The Foundation of PHP Testing
PHPUnit is the de facto standard for unit testing in PHP. It is included in Symfony, Laravel, and other popular frameworks.
Installation and Basic Test
composer require --dev phpunit/phpunitExample of a simple test for a calculator class:
// src/Calculator.phpclass Calculator { public function add($a, $b) { return $a + $b; }}
// tests/CalculatorTest.phpuse PHPUnit\\Framework\\TestCase;
class CalculatorTest extends TestCase { public function testAddition() { $calc = new Calculator(); $this->assertEquals(4, $calc->add(2, 2)); }}Working with Mock Objects
PHPUnit provides a built-in mechanism for creating stubs (mocks):
public function testUserRepository() { $mockRepo = $this->createMock(UserRepository::class); $mockRepo->method('findById') ->willReturn(new User(1, 'Alice'));
$service = new UserService($mockRepo); $result = $service->getUser(1); $this->assertEquals('Alice', $result->getName());}2. Pest — Elegant Syntax for Tests
Pest is a wrapper around PHPUnit that offers a more concise and readable syntax. It is ideal for projects where test writing speed is important.
Installation and Example
composer require --dev pestphp/pestCompare with PHPUnit: the same test in Pest looks like this:
// tests/CalculatorTest.phpit('calculates addition', function () { $calc = new Calculator(); expect($calc->add(2, 2))->toBe(4);});Advanced Features
- Higher Order Tests — call chains without nesting
- Architecture Testing — checking class structure
- Snapshot Testing — comparison with reference data
3. Codeception — For Integration and Functional Testing
Codeception combines unit, functional, and acceptance tests in one framework. It supports Selenium, Symfony, Laravel, Yii, and even WordPress.
Test Structure
codecept generate:cest acceptance LoginCestExample of a functional test for Laravel:
// tests/functional/UserCest.phpclass UserCest { public function testLogin(FunctionalTester $I) { $I->amOnPage('/login'); $I->fillField('email', 'user@example.com'); $I->fillField('password', 'secret'); $I->click('Login'); $I->see('Dashboard'); }}4. Additional Tools
- Mockery — a flexible library for mock objects (alternative to PHPUnit's built-in mocks)
- PHPStan / Psalm — static analysis that detects errors before running tests
- Infection — mutation testing: checks the quality of the tests themselves
- PHP CodeSniffer — ensures compliance with PSR-12
5. Practical Example: TDD for an API Service
Let's implement a simple TDD (Test-Driven Development) cycle for an order service:
Step 1. Write the Test
// tests/OrderServiceTest.phppublic function testCrea