4. Organizing Tests
One of the goals of PHPUnit is that tests should be composable: we want to be able to run any number or combination of tests together, for instance all tests for the whole project, or the tests for all classes of a component that is part of the project, or just the tests for a single class.
PHPUnit supports different ways of organizing tests and composing them into a test suite. This chapter shows the most commonly used approaches.
Composing a Test Suite Using the Filesystem
Probably the easiest way to compose a test suite is to keep all test case source files in a test directory. PHPUnit can automatically discover and run the tests by recursively traversing the test directory.
Lets take a look at the test suite of the sebastianbergmann/raytracer project.
Looking at this project’s directory structure, we see that the test case classes in the tests/unit
directory mirror the package and class structure of the System Under Test (SUT) in the src
directory:
src tests/unit
├── autoload.php ├── CameraTest.php
├── Camera.php ├── canvas
├── canvas │ ├── AnsiMapperTest.php
│ ├── AnsiMapper.php │ ├── CanvasTest.php
│ ├── CanvasIterator.php │ └── PortablePixmapMapperTest.php
│ ├── Canvas.php ├── ColorTest.php
│ ├── PortablePixmapMapper.php ├── intersection
│ └── WebpMapper.php │ ├── IntersectionCollectionTest.php
├── Color.php │ └── IntersectionTest.php
├── exceptions ├── material
│ ├── Exception.php │ ├── CheckersPatternTest.php
│ ├── IntersectionHasNoHitException.php │ ├── GradientPatternTest.php
│ ├── InvalidArgumentException.php │ ├── MaterialTest.php
│ ├── OutOfBoundsException.php │ ├── PatternTest.php
│ ├── RuntimeException.php │ ├── RingPatternTest.php
│ └── WorldHasNoLightException.php │ └── StripePatternTest.php
├── intersection ├── math
│ ├── IntersectionCollectionIterator.php │ ├── MatrixTest.php
│ ├── IntersectionCollection.php │ ├── RayTest.php
│ ├── Intersection.php │ ├── TransformationsTest.php
│ └── PreparedComputation.php │ └── TupleTest.php
├── material ├── PointLightTest.php
│ ├── CheckersPattern.php ├── shapes
│ ├── GradientPattern.php │ ├── PlaneTest.php
│ ├── Material.php │ ├── ShapeCollectionTest.php
│ ├── Pattern.php │ ├── ShapeTest.php
│ ├── RingPattern.php │ └── SphereTest.php
│ └── StripePattern.php └── WorldTest.php
├── math
│ ├── Matrix.php tests/integration
│ ├── Ray.php └── PuttingItTogetherTest.php
│ ├── Transformations.php
│ └── Tuple.php
├── PointLight.php
├── shapes
│ ├── Plane.php
│ ├── ShapeCollectionIterator.php
│ ├── ShapeCollection.php
│ ├── Shape.php
│ └── Sphere.php
└── World.php
The tests/integration
directory contains integration test cases that are kept separate from the tests/unit
directory’s unit tests.
To run all tests for this project can need to point the PHPUnit command-line test runner to the test directory:
$ ./tools/phpunit --bootstrap tests/bootstrap.php tests
PHPUnit 10.0.0 by Sebastian Bergmann and contributors.
Runtime: PHP 8.2.2
............................................................... 63 / 177 ( 35%)
............................................................... 126 / 177 ( 71%)
................................................... 177 / 177 (100%)
Time: 00:17.100, Memory: 28.27 MB
OK (177 tests, 657 assertions)
Note
If you point the PHPUnit command-line test runner to a directory it will look for *Test.php
files.
To run only the tests that are declared in the WorldTest
test case class in tests/unit/WorldTest.php
we can use the following command:
$ ./tools/phpunit --bootstrap src/autoload.php tests/unit/WorldTest.php
PHPUnit 10.0.0 by Sebastian Bergmann and contributors.
Runtime: PHP 8.2.2
............. 13 / 13 (100%)
Time: 00:00.095, Memory: 8.00 MB
OK (13 tests, 30 assertions)
For more fine-grained control of which tests to run we can use the --filter
option:
$ ./tools/phpunit --bootstrap src/autoload.php tests/unit --filter test_creating_a_world
PHPUnit 10.0.0 by Sebastian Bergmann and contributors.
Runtime: PHP 8.2.2
. 1 / 1 (100%)
Time: 00:00.077, Memory: 10.00 MB
OK (1 test, 2 assertions)
Composing a Test Suite Using XML Configuration
PHPUnit’s XML configuration file (The XML Configuration File) can also be used to compose a test suite. Example 4.1 shows a minimal phpunit.xml
file that will add all *Test
classes that are found in *Test.php
files when the tests
directory is recursively traversed.
Example 4.1 Composing a Test Suite Using XML Configuration
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/|version|/phpunit.xsd"
bootstrap="tests/bootstrap.php">
<testsuites>
<testsuite name="unit">
<directory>tests/unit</directory>
</testsuite>
<testsuite name="integration">
<directory>tests/integration</directory>
</testsuite>
</testsuites>
</phpunit>
Now that we have an XML configuration file, we can invoke the PHPUnit test runner without arguments (tests
, for instance) or options (--bootstrap
, for instance) to run our tests:
$ ./tools/phpunit
PHPUnit 10.0.0 by Sebastian Bergmann and contributors.
Runtime: PHP 8.2.2
Configuration: /path/to/raytracer/phpunit.xml
............................................................... 63 / 177 ( 35%)
............................................................... 126 / 177 ( 71%)
................................................... 177 / 177 (100%)
Time: 00:17.100, Memory: 28.27 MB
OK (177 tests, 657 assertions)
The PHPUnit test runner’s --list-suites
option can be used to print a list of all test suites defined in PHPUnit’s XML configuration file:
$ ./tools/phpunit --list-suites
PHPUnit 10.0.0 by Sebastian Bergmann and contributors.
Available test suite(s):
- unit
- integration
We can use the PHPUnit test runner’s --testsuite
option to limit the tests that are run to the tests of a specific test suite that is declared in the XML configuration file:
$ ./tools/phpunit --testsuite unit
PHPUnit 10.0.0 by Sebastian Bergmann and contributors.
Runtime: PHP 8.2.2
Configuration: /path/to/raytracer/phpunit.xml
............................................................... 63 / 172 ( 36%)
............................................................... 126 / 172 ( 73%)
.............................................. 172 / 172 (100%)
Time: 00:00.213, Memory: 24.27 MB
OK (172 tests, 637 assertions)