Organizing Tests
Running ./vendor/bin/phpunit tests/CalculatorTest.php works fine for one file, but naming
every test file on the command line stops being practical the moment a project has more than a
handful of them. A phpunit.xml configuration file at the project root fixes that — it
tells PHPUnit where your tests live (and a number of other settings), so running the whole suite is
just ./vendor/bin/phpunit with no arguments at all.
The phpunit.xml Configuration File
PHPUnit automatically looks for a file named phpunit.xml (or phpunit.xml.dist,
conventionally used when the file is meant to be committed and shared by the whole team) in the
project root. A minimal, annotated example that points PHPUnit at a tests/ directory:
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/autoload.php"
colors="true">
<testsuites>
<testsuite name="default">
<directory>tests</directory>
</testsuite>
</testsuites>
</phpunit>
| Attribute / element | What it does |
|---|---|
bootstrap | A PHP file loaded before any test runs — almost always Composer's autoloader |
colors="true" | Colorizes pass/fail output in the terminal |
<testsuites> | One or more named groups of tests |
<directory>tests</directory> | Every test class PHPUnit should discover and run, found anywhere under this folder |
With this file in place, the entire suite runs with just:
./vendor/bin/phpunit
Splitting Tests into Named Suites
Larger projects often want to separate fast unit tests from slower integration tests, so a developer
can run just the quick ones while coding and save the full suite for CI. A phpunit.xml
can define more than one <testsuite>, each pointing at its own directory:
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/autoload.php">
<testsuites>
<testsuite name="Unit">
<directory>tests/Unit</directory>
</testsuite>
<testsuite name="Integration">
<directory>tests/Integration</directory>
</testsuite>
</testsuites>
</phpunit>
With named suites defined, you can run just one of them by name when you only want the fast feedback loop:
# Run only the fast unit suite while developing
./vendor/bin/phpunit --testsuite Unit
# Run everything, including the slower integration suite
./vendor/bin/phpunit
Mirroring src/ Inside tests/
A common, easy-to-navigate convention is to give your tests/ directory the exact same
folder structure as your src/ directory, with each class's test file named after the
class it covers, suffixed with Test. Finding the test for any given class (or the class
for any given test) then becomes a matter of pattern-matching the path.
src/
Services/
PaymentService.php
Models/
Order.php
tests/
Services/
PaymentServiceTest.php
Models/
OrderTest.php
src/Services/PaymentService.php is tested by tests/Services/PaymentServiceTest.php
— same relative path, same base filename, just with Test appended and living under
tests/ instead of src/. As a codebase grows to dozens or hundreds of classes,
this mirrored structure is what keeps "where's the test for this?" a five-second question instead of a
search.