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 we need to point the PHPUnit command-line test runner to the test directory:
$ ./tools/phpunit --bootstrap tests/bootstrap.php tests PHPUnit 10.3.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.3.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.3.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.
<?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.3.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.3.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.3.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)