What it is
PHPUnit is the standard testing framework for PHP, with assertions, test doubles, data providers and code coverage reporting.
Test classes extend TestCase. Attributes replace the older annotations, and data providers drive parameterised tests.
Installation
composer require --dev phpunit/phpunitGetting started
The smallest useful thing you can do with it, and what each part means.
php
<?php
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\Attributes\{Test, DataProvider};
final class SlugifyTest extends TestCase
{
private Slugifier $sut;
protected function setUp(): void
{
$this->sut = new Slugifier(); // fresh per test
}
#[Test]
public function it_handles_empty_input(): void
{
self::assertSame('', $this->sut->slugify(''));
}
#[Test]
#[DataProvider('cases')]
public function it_slugifies(string $input, string $expected): void
{
self::assertSame($expected, $this->sut->slugify($input));
}
public static function cases(): iterable
{
yield 'spaces' => ['Hello World', 'hello-world'];
yield 'punctuation' => ['C++ & Go!', 'c-go'];
}
}Advanced usage
Where the library earns its place over a simpler alternative.
php
<?php
#[Test]
public function it_returns_the_title(): void
{
$repo = $this->createMock(BookRepository::class);
$repo->expects(self::once())
->method('find')
->with(42)
->willReturn(new Book('Dune'));
$service = new BookService($repo);
self::assertSame('Dune', $service->title(42));
}
#[Test]
public function it_rejects_a_missing_book(): void
{
$this->expectException(NotFoundException::class);
$this->expectExceptionMessage('Book 99 not found');
(new BookService($this->createStub(BookRepository::class)))->title(99);
}Errors and fixes
The failures you are most likely to hit, and what actually resolves them.
- Test passes but asserts nothing
- A stub was used where a mock with expects() was needed, or the assertion is unreachable. Enable failOnRisky in the configuration.
- Annotations stopped working after upgrading
- PHPUnit 11 removed docblock annotations. Migrate to attributes such as #[Test] and #[DataProvider].
Best practices
- Prefer assertSame over assertEquals — loose comparison hides type bugs.
- Use attributes rather than docblock annotations; annotations are removed in PHPUnit 11+.
- Use data providers instead of near-duplicate test methods.
- Consider Pest if the team prefers a lighter syntax; it runs on PHPUnit underneath.
Background
Why it exists, and what it was reacting to.
Written by Sebastian Bergmann, PHPUnit is the foundation of PHP testing — Laravel's and Symfony's own test tooling both build on it.
