testing php applications
Test Organisation And Naming
A test suite is production code for developers. Clear file structure and names make failures faster to diagnose and make missing scenarios easier to notice during review.
Name The Behaviour
A useful test name describes the situation and expected outcome: testCheckoutRejectsExpiredDiscountCode() communicates more than testDiscountCode(). Keep one main reason to fail in each test.
Organise By The Codebase
Mirror the repository where that helps navigation: unit tests for domain rules, integration tests for repositories or adapters, and feature or HTTP tests for application workflows. Keep shared test helpers small; a deep inheritance tree can hide important setup.
<?php
declare(strict_types=1);
use PHPUnit\Framework\TestCase;
final class CheckoutTest extends TestCase
{
public function testCheckoutRejectsExpiredDiscountCode(): void
{
// Arrange the expired code, submit checkout, and assert the rejection.
}
}
The method name makes the failed behaviour visible in CI output. The body should arrange only the state needed for that scenario.
Common Mistakes
- Names such as
testItWorks()that reveal nothing when CI fails. - One giant test that checks several unrelated rules.
- Copy-pasted setup that obscures the meaningful state.
- Helpers so abstract that readers cannot see the scenario.
What To Practise
- Name tests after behaviour.
- Group tests at useful boundaries.
- Keep setup readable and local when possible.
Practice
Practice: Rename A Weak Test List
Improve a set of vague test names for a product publishing feature.
Requirements
- Cover successful publishing.
- Cover a missing title.
- Cover an ordinary user attempting an admin action.
- Keep each name focused on one outcome.
Show solution
The names should explain CI failures before the file is opened.
<?php
declare(strict_types=1);
$testNames = [
'testAdminCanPublishValidProduct',
'testPublishingRejectsProductWithoutTitle',
'testOrdinaryUserCannotPublishProduct',
];
foreach ($testNames as $name) {
echo $name . PHP_EOL;
}
// Prints:
// testAdminCanPublishValidProduct
// testPublishingRejectsProductWithoutTitle
// testOrdinaryUserCannotPublishProduct
Each test name identifies the actor or input condition and one expected result.