3. The Command-Line Test Runner
The PHPUnit command-line test runner can be invoked through the phpunit command.
Running tests
The most common way to run tests is with a phpunit.xml configuration file (see Composing a Test Suite Using XML Configuration). When a configuration file is present in the current working directory, simply running phpunit without any arguments will execute all configured test suites:
$ ./tools/phpunit PHPUnit 13.4.0 by Sebastian Bergmann and contributors. Runtime: PHP 8.5.3 Configuration: /path/to/project/phpunit.xml .. 2 / 2 (100%) Time: 00:00.077, Memory: 10.00 MB OK (2 tests, 2 assertions)
You can also run the tests of a specific test source file by passing its path as an argument:
$ ./tools/phpunit tests/ExampleTest.php PHPUnit 13.4.0 by Sebastian Bergmann and contributors. Runtime: PHP 8.5.3 .. 2 / 2 (100%) Time: 00:00.077, Memory: 10.00 MB OK (2 tests, 2 assertions)
When invoked as shown above, the PHPUnit command-line test runner will look for a ExampleTest.php source code file in the current working directory, load it, and expect to find an ExampleTest test case class. It will then run the tests found in that class.
PHPT tests
In addition to test classes, PHPUnit’s test runner can also execute PHPT tests.
PHPT is a simple test format used by the PHP project itself.
Files with the .phpt suffix are automatically discovered by the test runner (see the --test-suffix option in Selection).
This can be useful, for instance, for end-to-end tests of CLI tools or for getting basic regression tests in place for legacy codebases.
PHPT tests are documented in PHPT Tests.
Selecting tests
PHPUnit provides several command-line options for selecting which tests to run.
Filtering by test name
The --filter option selects tests whose name matches a pattern:
$ ./tools/phpunit --filter testDoesSomething PHPUnit 13.4.0 by Sebastian Bergmann and contributors. Runtime: PHP 8.5.3 Configuration: /path/to/project/phpunit.xml . 1 / 1 (100%) Time: 00:00.077, Memory: 10.00 MB OK (1 test, 1 assertion)
The pattern is matched against the full test name, which has one of these forms:
Fully\Qualified\ClassName::testMethodName Fully\Qualified\ClassName::testMethodName with data set #0 Fully\Qualified\ClassName::testMethodName with data set "named set"
Matching is a partial match (the pattern does not need to cover the entire name) and not anchored. For example, --filter testAdd also matches a test named testAddress.
PHPT tests (see PHPT tests) are never selected by --filter.
The --exclude-filter option accepts the same pattern syntax described below and removes matching tests from the run. It can be combined with --filter.
Shortcut syntax
For common cases you do not need to write a regular expression. The following shortcut forms are recognized and translated into an appropriate pattern internally:
testMethodNameSelects all tests whose name contains
testMethodName.ClassName::testMethodNameSelects a specific test method in a specific class.
testMethodName#3Selects the test method together with data set
#3(a numeric data set key).testMethodName#1-3Selects the test method together with numeric data sets in the inclusive range
1to3.#1-3Selects numeric data sets in the inclusive range
1to3across all test methods.testMethodName@one plus oneSelects the test method together with the named data set
"one plus one". Quote the argument so the shell preserves spaces.testMethodName@one.*Selects the test method together with any named data set that matches the regular expression
one.*.testMethodName#one plus oneSelects the test method together with the named data set
"one plus one". This form is equivalent totestMethodName@one plus oneand is accepted so that the test ID format produced by--list-test-idscan be passed directly to--filter. Quote the argument so the shell preserves spaces.
When the shortcut form is used, matching is case-insensitive.
Regular expression syntax
If the pattern begins with a non-alphanumeric character and is a valid PCRE regular expression, it is used verbatim (including its delimiters and modifiers). This form is useful when the shortcut syntax is not expressive enough, or when you need case-sensitive matching:
$ ./tools/phpunit --filter '/^App\\Domain\\.*Test::test/'
Caution
Fully-qualified class names contain backslashes, and backslashes are special in regular expressions. If you want to use --filter with a fully-qualified class name, either escape every backslash (App\\\\Domain\\\\FooTest) or wrap the pattern with explicit delimiters (/App\\\\Domain\\\\FooTest/). The filter is not passed through preg_quote(), so all regular expression metacharacters remain active.
Filtering by test suite
When you have multiple test suites defined in your XML configuration file, you can use the --testsuite option to run only the tests of a specific suite:
$ ./tools/phpunit --testsuite unit PHPUnit 13.4.0 by Sebastian Bergmann and contributors. Runtime: PHP 8.5.3 Configuration: /path/to/project/phpunit.xml ............... 15 / 15 (100%) Time: 00:00.100, Memory: 10.00 MB OK (15 tests, 30 assertions)
The --exclude-testsuite option excludes a named test suite from the run.
The --list-suites option prints a list of all test suites defined in the XML configuration file without running any tests.
Filtering by group
The --group option runs only the tests that belong to a specified group. Groups are assigned to tests using the #[Group] attribute (see Group).
The --exclude-group option excludes tests belonging to a specified group.
Both options can be used more than once. A test is run when it is in any of the groups selected with --group, and it is not run when it is in any of the groups selected with --exclude-group:
$ ./tools/phpunit --group database --group network
The command shown above runs the tests that are in the group database, the tests that are in the group network, and the tests that are in both.
Selecting the tests that are in every one of several groups
Separating the names of groups with + selects the tests that are in all of them:
$ ./tools/phpunit --group database+slow
The command shown above runs only the tests that are in the group database and in the group slow. A test that is in only one of them is not run.
Any number of group names can be combined this way, and such a combination can be used wherever a single group name can be used. The two ways of combining groups can be mixed: each value of the option is a combination of groups a test has to be in, and a test is run when it matches at least one of these values.
$ ./tools/phpunit --group database+slow --group smoke
The command shown above runs the tests that are in both database and slow, as well as the tests that are in smoke.
--exclude-group works the same way:
$ ./tools/phpunit --exclude-group database+slow
The command shown above runs every test except the ones that are in both database and slow. A test that is only in database, or only in slow, is still run.
Caution
Because + combines group names, it cannot also be part of a group name. A group named database+slow cannot be selected: --group database+slow asks for the tests that are in database and in slow, not for the tests that are in the group whose name is database+slow.
PHPUnit emits a warning where such a group name is declared so that this does not go unnoticed. Only a + between two group names is treated as a combination, though: +name, name+, and + are ordinary group names and can be selected as before.
Filtering by covered unit
The --covers option runs only tests that declare they intend to cover a specified class or function. The --uses option runs only tests that declare they intend to use a specified class or function.
Filtering by test ID
A test ID is the canonical, fully qualified identifier of a single test. It has one of these forms:
Fully\Qualified\ClassName::testMethodName Fully\Qualified\ClassName::testMethodName#0 Fully\Qualified\ClassName::testMethodName#named data set
Test IDs are matched strictly and exactly; they are not patterns.
The --run-test-id option runs the single test identified by the given test ID:
$ ./tools/phpunit --run-test-id 'PHPUnit\TestFixture\ExampleTest::testOne#named data set'
The --test-id-filter-file option runs the tests whose IDs are listed in the specified file, with one test ID per line. Together with --list-test-ids (see Listing tests) this enables a list/filter/run workflow that is useful, for example, for splitting a test suite across parallel CI jobs:
List all test IDs:
phpunit --list-test-ids > test-ids.txtSplit or filter the file into batches
Run a batch:
phpunit --test-id-filter-file batch-1.txt
Listing tests
The --list-tests option prints a list of all tests that would be executed without actually running them. This is useful for verifying that your selection options are working as expected.
The --list-test-ids option prints a list of all tests that would be executed as test IDs (one per line) without actually running them. The output of this option is suitable for use with --test-id-filter-file and --run-test-id (see Filtering by test ID).
The --list-tests-xml option writes this list in XML format to a specified file.
See Selection for the complete reference of all test selection options.
Outcome and Issues
PHPUnit separates the outcome (errored, failed, incomplete, skipped, or passed) of a test from the issues (considered risky, triggered a warning, …) of a test.
With regard to outcome, PHPUnit distinguishes between failures and errors. A test fails when an assertion failed. This is different from an unexpected exception or a PHP error that occur while a test is running. When this happens, the test errors.
Errors tend to be easier to fix than failures. If you have a big list of problems, it is best to tackle the errors first and see if you have any failures left when they are all fixed.
Output
The default output of PHPUnit’s test runner consists of up to four sections.
Runtime information
This section contains information about the PHPUnit version, PHP version, and PHPUnit’s XML configuration file.
This section can be disabled using the --no-output CLI option.
Progress
This section is printed to indicate progress while the tests are being run.
For each test run, the PHPUnit command-line tool prints one character to indicate progress:
.
Printed when a successful test has no issues
F
Printed when an assertion fails while running the test method
E
Printed when an error occurs while running the test method
W
Printed when the test triggered a warning
R
Printed when the test has been considered risky (see Risky Tests)
D
Printed when the test triggered a deprecation
N
Printed when the test triggered a notice
I
Printed when the test is marked as incomplete (see Incomplete tests)
S
Printed when the test was skipped (see Skipping tests)
This section can be disabled using the --no-progress and --no-output CLI options.
Test results
This section contains information about errors, failures, skipped tests, incomplete tests, and issues. By default, only information about errors and failures is printed.
This section is only printed when there are errors, failures, or issues to report. It can be disabled using the --no-results and --no-output CLI options.
Failure output
Whenever a test fails, PHPUnit tries its best to provide you with as much context as possible that can help to identify the problem.
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
final class ArrayDiffTest extends TestCase
{
public function testEquality(): void
{
$this->assertSame(
[1, 2, 3, 4, 5, 6],
[1, 2, 33, 4, 5, 6],
);
}
}
Running the test shown above yields the output shown below:
./tools/phpunit tests/ArrayDiffTest.php
PHPUnit 13.4.0 by Sebastian Bergmann and contributors.
Runtime: PHP 8.5.11
F 1 / 1 (100%)
Time: 00:00, Memory: 32.33 MB
There was 1 failure:
1) ArrayDiffTest::testEquality
Failed asserting that two arrays are identical.
--- Expected
+++ Actual
@@ @@
Array &0 [
0 => 1,
1 => 2,
- 2 => 3,
+ 2 => 33,
3 => 4,
4 => 5,
5 => 6,
]
/path/to/tests/ArrayDiffTest.php:8
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
In this example only one of the array values differs and the other values are shown to provide context on where the error occurred.
When the generated output would be long to read PHPUnit will split it up and provide a few lines of context around every difference.
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
final class LongArrayDiffTest extends TestCase
{
public function testEquality(): void
{
$this->assertSame(
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 33, 4, 5, 6],
);
}
}
Running the test shown above yields the output shown below:
./tools/phpunit tests/LongArrayDiffTest.php
PHPUnit 13.4.0 by Sebastian Bergmann and contributors.
Runtime: PHP 8.5.11
F 1 / 1 (100%)
Time: 00:00, Memory: 32.33 MB
There was 1 failure:
1) LongArrayDiffTest::testEquality
Failed asserting that two arrays are identical.
--- Expected
+++ Actual
@@ @@
11 => 0,
12 => 1,
13 => 2,
- 14 => 3,
+ 14 => 33,
15 => 4,
16 => 5,
17 => 6,
]
/path/to/tests/LongArrayDiffTest.php:8
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
Edge cases
When a comparison fails PHPUnit creates textual representations of the input values and compares those. Due to that implementation a diff might show more problems than actually exist.
This only happens when using assertEquals() or other “weak” comparison
functions on arrays or objects.
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
final class ArrayWeakComparisonTest extends TestCase
{
public function testEquality(): void
{
$this->assertEquals(
[1, 2, 3, 4, 5, 6],
['1', 2, 33, 4, 5, 6],
);
}
}
Running the test shown above yields the output shown below:
./tools/phpunit tests/ArrayWeakComparisonTest.php
PHPUnit 13.4.0 by Sebastian Bergmann and contributors.
Runtime: PHP 8.5.11
F 1 / 1 (100%)
Time: 00:00, Memory: 32.33 MB
There was 1 failure:
1) ArrayWeakComparisonTest::testEquality
Failed asserting that two arrays are equal.
--- Expected
+++ Actual
@@ @@
Array (
- 0 => 1
+ 0 => '1'
1 => 2
- 2 => 3
+ 2 => 33
3 => 4
4 => 5
5 => 6
)
/path/to/tests/ArrayWeakComparisonTest.php:8
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
In this example the difference in the first index between
1 and '1' is
reported even though assertEquals() considers the values as a match.
Summary
This section contains a summary of the test suite execution.
When no test errored or failed then the summary will be
OKWhen a test errored then the summary will be
ERRORED!When a test failed and no test errored then the summary will be
FAILED!When there were no errors or failures, but a test was skipped, then the summary will be
OK, but some tests were skipped!When there were no errors, failures, or skipped tests, but there were issues, then the summary will be
OK, but there were issues!
--fail-on-*
Please note that if you use CLI options such as --fail-on-deprecation, for example,
or their XML configuration counterparts, then this only affects the test runner’s shell
exit code. It does not have an effect on whether or not OK will be printed in the
output’s summary section.
This section can be disabled using the --no-output CLI option.
Alternative output formats
PHPUnit supports alternative output formats that replace the default progress and result output.
TestDox
The --testdox option replaces the default result output with TestDox format.
See TestDox for details.
Debug
The --debug option replaces the default progress and result output with detailed event-by-event information about the test execution process. When combined with --with-telemetry, timing and memory usage information is included.
TeamCity
The --teamcity option replaces the default progress and result output with TeamCity format. This is used for integration with PhpStorm or TeamCity.
Compact
The --compact option replaces the default progress and result output with a compact format that is stripped down to what is actionable: there is no progress section, no timing or memory usage line, and no use of color. When all tests pass, only the runtime information section and a single summary line are printed:
$ ./tools/phpunit --compact tests PHPUnit 13.4.0 by Sebastian Bergmann and contributors. Runtime: PHP 8.5.5 OK (4699 tests, 13576 assertions)
When tests fail or have other issues, each defect is printed as a clearly delimited block with the test name, the assertion message, and the file location:
$ ./tools/phpunit --compact tests PHPUnit 13.4.0 by Sebastian Bergmann and contributors. Runtime: PHP 8.5.5 --- FAILURE: ExampleTest::testSomething Failed asserting that false is true. /path/to/tests/ExampleTest.php:47 FAILURES (23 tests, 42 assertions, 1 failure)
The summary line begins with OK when all tests pass, FAILURES when at least one test failed, and ERRORS when at least one test errored. The counts that follow include the number of tests run and assertions made as well as, when non-zero, the number of errors, failures, deprecations, PHPUnit deprecations, warnings, PHPUnit warnings, notices, PHPUnit notices, skipped tests, incomplete tests, and risky tests.
Errors and failures are printed as soon as they occur, before the summary line. All other defects are listed after the summary line, in this order: errors, warnings, deprecations, and notices reported by PHPUnit itself, deprecations, warnings, notices, errors triggered by tests, risky tests, incomplete tests, skipped tests.
When the time limit for the test run configured with --timeout (see Limiting the duration of the test run) was exceeded, this is printed as a record of its own directly after the summary line, so that it cannot be mistaken for the output of a test:
ERRORS (23 tests, 42 assertions, 1 error)
--- TIME LIMIT EXCEEDED
The time limit of 300 seconds for the test run was exceeded.
By default, only errors, failures, risky tests, and the errors and warnings reported by PHPUnit itself are shown in detail. Like the default output, --compact respects the --display-* flags described under Controlling output: use --display-deprecations, --display-warnings, --display-notices, --display-errors, --display-incomplete, --display-skipped, --display-phpunit-deprecations, --display-phpunit-notices, or --display-all-issues to display additional details. Issues that are only counted in the summary line are not shown in detail unless the corresponding flag is used.
Compact output can also be activated by setting the PHPUNIT_COMPACT_OUTPUT environment variable to 1. This makes it easy to enable compact output globally without changing how PHPUnit is invoked, for example when running tests inside an AI-based coding assistant where every token of test output consumes context window budget.
Note
When both the PHPUNIT_COMPACT_OUTPUT environment variable and the --compact CLI option are set, compact output is used. The CLI option --compact cannot be combined with --teamcity, --testdox, --testdox-summary, --debug, or --no-output.
Controlling output
The following options control what is displayed in the test results section. By default, only information about errors and failures is printed. Use these options to display additional details:
--display-incompleteshows details for incomplete tests--display-skippedshows details for skipped tests--display-deprecationsshows details for deprecations triggered by tests--display-noticesshows details for notices triggered by tests--display-warningsshows details for warnings triggered by tests--display-errorsshows details for errors triggered by tests--display-phpunit-deprecationsshows details for deprecations reported by PHPUnit itself--display-phpunit-noticesshows details for notices reported by PHPUnit itself--display-all-issuesenables all of the above
The --colors=<flag> option controls the use of colors in terminal output. Accepted values are never, auto (uses colors when the terminal supports it), and always.
The --columns option controls the width of the progress output. Use --columns max to use the full terminal width.
The --reverse-list option prints defects in reverse order, showing the most recently encountered defects first.
See Reporting for the complete reference of all reporting options.
Interaction between output options
The options described above fall into two groups:
--no-output,--no-progress, and--no-resultssuppress sections of the default output format described in Output.--testdox,--teamcity,--compact, and--debugselect an alternative output format that replaces the default progress and result output.
The --no-* options apply to the default output format only. They do not suppress the output produced by an alternative output format. For example, --testdox --no-output will still produce TestDox output.
The --testdox-html and --testdox-text options are logging options (see Logging): they write a TestDox-formatted report to a file. They are not intended to replace or augment the console output. Use --testdox when you want TestDox-formatted output on the console.
Exit codes
The PHPUnit command-line test runner exits with an exit code that indicates the outcome of the test run. This is particularly important for Continuous Integration (CI) pipelines and scripts that need to act on the result of a test run.
0
All tests passed (no errors, no failures)
1
At least one test failed
2
At least one test errored
124
The time limit for the test run configured with
--timeoutwas exceeded (see Limiting the duration of the test run)
By default, issues such as deprecations, notices, warnings, and risky tests do not affect the exit code. The --fail-on-* family of options changes this behaviour:
--fail-on-warningcauses a failure exit code when any test triggers a warning--fail-on-riskycauses a failure exit code when any test is considered risky--fail-on-deprecationcauses a failure exit code when any test triggers a deprecation--fail-on-noticecauses a failure exit code when any test triggers a notice--fail-on-skippedcauses a failure exit code when any test is skipped--fail-on-incompletecauses a failure exit code when any test is marked incomplete--fail-on-empty-test-suitecauses a failure exit code when no tests were executed--fail-on-all-issuesenables all of the above
These options are commonly used in CI pipelines to enforce strict quality standards:
$ ./tools/phpunit --fail-on-deprecation --fail-on-notice --fail-on-warning PHPUnit 13.4.0 by Sebastian Bergmann and contributors. Runtime: PHP 8.5.3 Configuration: /path/to/project/phpunit.xml DW 2 / 2 (100%) Time: 00:00.077, Memory: 10.00 MB OK, but there were issues! Tests: 2, Assertions: 2, Deprecations: 1, Warnings: 1.
--fail-on-all-issues and its XML configuration file counterpart,
failOnAllIssues="true", take precedence over the fine-grained failOn*
attributes of the XML configuration file: configuring a fine-grained
failOn* attribute to false has no effect when failOnAllIssues
is configured to true.
The fine-grained --fail-on-* options have --do-not-fail-on-*
counterparts that can be used to override settings from the XML configuration
file. A --do-not-fail-on-* option disables failing on the respective issue
type and takes precedence over all --fail-on-* options and
failOn* attributes, including --fail-on-all-issues and
failOnAllIssues="true". The --do-not-fail-on-* options are therefore
the only way to not fail on a specific issue type when
failOnAllIssues="true" is configured.
For deprecations, the failure behaviour can be refined based on how a deprecation was triggered. The --fail-on-self-deprecation, --fail-on-direct-deprecation, and --fail-on-indirect-deprecation options (and their --do-not-fail-on-* counterparts) control the exit code separately for each trigger type. See Failing on deprecations based on how they are triggered for details.
See Execution for the complete reference.
Stopping early
When a test suite is large, it can be useful to stop execution as soon as a problem is encountered instead of waiting for the entire suite to finish.
The --stop-on-* family of options controls this:
--stop-on-defect[=<n>]stops after the first (or n-th) error, failure, warning, or risky test--stop-on-error[=<n>]stops after the first (or n-th) error--stop-on-failure[=<n>]stops after the first (or n-th) failure--stop-on-warning[=<n>]stops after the first (or n-th) warning--stop-on-risky[=<n>]stops after the first (or n-th) risky test--stop-on-deprecation[=<n>]stops after the first (or n-th) deprecation--stop-on-notice[=<n>]stops after the first (or n-th) notice--stop-on-skipped[=<n>]stops after the first (or n-th) skipped test--stop-on-incomplete[=<n>]stops after the first (or n-th) incomplete test
When used without a value, these options stop execution after the first occurrence. When a threshold
n is specified (for example, --stop-on-failure=3), execution continues until n occurrences
have been encountered.
See Execution for the complete reference.
Limiting the duration of the test run
The --timeout <sec> option sets a wall-clock limit, in seconds, for the entire test run:
$ ./tools/phpunit --timeout 300
The clock starts as soon as the configuration is known, so bootstrapping and loading the test suite count toward the limit. The limit is checked whenever a test has finished; no PHP extension is required for this. When the limit is exceeded, no further tests are started and the run ends the way a run that was stopped by a --stop-on-* option ends: the configured logs are written, the result is printed, and a message states that the time limit was exceeded.
When the pcntl extension is available, a test that is still running when the limit is exceeded is aborted. It is reported as an error whose stack trace shows where the test was stuck:
$ ./tools/phpunit --timeout 300 PHPUnit 13.4.0 by Sebastian Bergmann and contributors. Runtime: PHP 8.5.5 Configuration: /path/to/project/phpunit.xml .......E The time limit of 300 seconds for the test run was exceeded. Time: 05:00.012, Memory: 12.00 MB There was 1 error: 1) ExampleTest::testSomething PHPUnit\Runner\TimeLimit\TimeLimitExceededException: This test was aborted because the time limit of 300 seconds for the test run was exceeded /path/to/tests/ExampleTest.php:47 ERRORS! Tests: 8, Assertions: 7, Errors: 1.
Without the pcntl extension, the test that is running when the limit is exceeded is not aborted. The test run stops after it has finished. The same applies to tests that run in a separate process (see RunInSeparateProcess) and to PHPT tests, regardless of whether pcntl is available.
The shell exit code of a run that exceeded its time limit is 124, the value that GNU timeout(1) uses for a command that exceeded its time limit, regardless of whether the tests that did run passed (see Exit codes). Code coverage reports and the baseline are not generated for such a run, as for a run that was interrupted by a signal. The PHPUnit\Event\TestRunner\TimeLimitExceeded event (see Events) is emitted when the limit is exceeded.
--timeout limits the test run as a whole, whereas --enforce-time-limit limits individual tests based on their size (see Test Execution Timeout). The two options can be combined.
--timeout is intended for situations in which PHPUnit is invoked without a person watching the run, for instance by a CI pipeline or by an AI-based coding assistant. Such a caller cannot tell a hung test run from a long one. Without a time limit, its only option is to kill the process, which leaves it without a summary, an exit code, a log file, or any record of which test was running. With --timeout, the run ends on its own with all of these.
Logging
PHPUnit can write test results to log files in various formats:
--log-otr <file>writes test results in Open Test Reporting (OTR) XML format (recommended)--log-junit <file>writes test results in JUnit XML format (legacy)--log-teamcity <file>writes test results in TeamCity format (used by PhpStorm)--testdox-html <file>writes test results in TestDox format as an HTML file--testdox-text <file>writes test results in TestDox format as a plain text file--log-events-text <file>writes all test runner events as plain text--log-events-verbose-text <file>writes all test runner events with extended information
These logfiles are commonly used for CI tool integration and reporting.
The --testdox-html and --testdox-text options write a report to a file; they are not intended to replace or augment the console output. For TestDox-formatted console output use --testdox (see TestDox).
Logging can also be configured in the XML configuration file (see The <logging> Element).
The --no-logging option ignores all logging configured in the XML configuration file.
Test execution order
By default, PHPUnit executes the tests of each test suite in the order in which it discovered them. The --order-by option changes that: it configures a pipeline of reordering stages that is applied to the tests of every test suite, one stage after another.
Order
The order is the first stage of the pipeline. Exactly one order can be configured:
default
Tests are executed in the order in which they were discovered
duration-ascending
Tests are ordered by duration, fastest test first. This requires the test run history (enabled by default, see
--record-test-run-history)
duration-descending
Tests are ordered by duration, slowest test first. This requires the test run history
modified-ascending
Tests are ordered by the time their source files were last modified, least recently modified test first
modified-descending
Tests are ordered by the time their source files were last modified, most recently modified test first
random
Tests are executed in random order. Use
--random-order-seedto make the order reproducible
reverse
Tests are executed in reverse discovery order
size-ascending
Tests are ordered by size (small, medium, large, unknown)
size-descending
Tests are ordered by size in reverse (large, medium, small, unknown)
Ordering by modification time
--order-by modified-descending runs the tests you have worked on most recently first. Combined with --stop-on-failure, this shortens the feedback loop while a new test is being written.
The source files of a test are the files a change to which can change what the test does: the file that declares the test class, the files that declare the classes it extends, and the files that declare the traits any of them use. The most recent modification among them decides, so modifying a base test class or a trait moves every test class that uses it to the front as well. For a PHPT test, the .phpt file is the test. A test suite is as new as the newest test it contains.
Note
The modification time of a file is not a record of who changed what: checking out a branch, cloning a repository, installing dependencies, and copying or deploying files all set it to the time of that operation. On a continuous integration server, where every file is usually written at the same time, this order is therefore meaningless. It is intended for local development and should not be configured in a project’s XML configuration file.
Many file systems store modification times with a resolution of one second, so files that were written during the same second are equally new. Tests that are equally new keep the order that they had before this stage was applied.
Running previously defective tests first
The order can be followed by defects, for instance --order-by duration-ascending,defects. This adds a second stage that moves the tests that were defective during the previous test run to the front, so that a defect is reached as fast as possible. Tests that are not moved to the front keep the order that the first stage established.
Only tests that errored or failed count as defects. A test that was skipped, that was marked as incomplete, or that triggered an issue is not moved to the front. A test suite counts as defective when at least one of the tests it contains counts as defective, so that defects are moved to the front through arbitrarily deeply nested test suites as well.
--order-by defects can also be used on its own, without configuring an order.
Note
Ordering by defects and ordering by duration are based on the test run history.
When the recording of the test run history is disabled, for instance using the
--do-not-record-test-run-history CLI option or the recordTestRunHistory="false"
attribute in the XML configuration file, the tests cannot be ordered this way and PHPUnit
emits a test runner warning:
Tests cannot be ordered by defects because recording of the test run history is disabled
The tests are then executed in the order that the remaining stages, if any, lead to.
Resolving dependencies between tests
Dependency resolution is the last stage of the pipeline: it reorders the tests of a test suite so that as many Depends* declarations as possible are satisfied. It is enabled by default and is controlled using the --resolve-dependencies and --ignore-dependencies CLI options, or the resolveDependencies attribute in the XML configuration file.
Because any stage that ran after it would undo the order it establishes, dependency resolution is always applied last.
Aliases
Convenience aliases are available: --random-order (for --order-by random) and --reverse-order (for --order-by reverse).
$ ./tools/phpunit --order-by random --random-order-seed 42 PHPUnit 13.4.0 by Sebastian Bergmann and contributors. Runtime: PHP 8.5.3 Configuration: /path/to/project/phpunit.xml Random seed: 42 .. 2 / 2 (100%) Time: 00:00.077, Memory: 10.00 MB OK (2 tests, 2 assertions)
Deprecated ways of configuring the execution order
The values below are still accepted by --order-by and by the executionOrder attribute in the XML configuration file, but PHPUnit emits a test runner deprecation for them:
depends and no-depends
Whether dependencies between tests are resolved is not an order. Use the
--resolve-dependenciesand--ignore-dependenciesCLI options, or theresolveDependenciesattribute in the XML configuration file, instead.
defects written before the order
defects,duration-ascendingandduration-ascending,defectscurrently mean the same thing: the order is applied first and defects are moved to the front afterwards. In PHPUnit 14, the stages will be applied in the order in which they are written, and the two values will therefore mean different things. Write the order first, for instanceduration-ascending,defects.
More than one order
For instance
duration-ascending,reverse. The order that is written last currently overrides the ones written before it. This will be an error in PHPUnit 14.
duration and size
Use
duration-ascendingandsize-ascendinginstead.
A value that PHPUnit does not know is rejected with an error when it is used for --order-by. When it is used for the executionOrder attribute then it is currently ignored, which is deprecated and will be an error in PHPUnit 14.
See Execution for the complete reference.