Skip to content

PHPUnit

testflake/phpunit is a Composer package. It needs no Node, npm, or JavaScript toolchain — it talks to the TestFlake API directly over HTTP.

There is also nothing to register in phpunit.xml. PHPUnit already exposes everything TestFlake needs as command-line flags, so unlike the Playwright integration there is no reporter or extension to add.

PHP 7.2 or later, with the curl, dom and json extensions.

PHPUnit itself is not a dependency. The client wraps whichever phpunit you already run, so it stays out of your version constraints.

ext-zlib is optional but worth having: with it, result payloads are gzipped before upload. The client detects it at runtime and sends them uncompressed otherwise.

  1. Install the package

    Terminal window
    composer require --dev testflake/phpunit
  2. Wrap the command you already run

    Terminal window
    TESTFLAKE_KEY=your-key TESTFLAKE_HOST=https://app.testflake.com \
    vendor/bin/testflake vendor/bin/phpunit

    Your own flags pass straight through, and the suite is enumerated with them applied — --testsuite, --group and --filter all behave as usual:

    Terminal window
    vendor/bin/testflake vendor/bin/phpunit --testsuite=integration

--shard=N/M is read by the wrapper and never reaches PHPUnit, which has no such flag of its own. It goes before the command being wrapped:

Terminal window
TESTFLAKE_KEY=your-key vendor/bin/testflake --shard=2/8 vendor/bin/phpunit

On GitLab CI you can leave it out entirely — the wrapper reads CI_NODE_INDEX and CI_NODE_TOTAL:

.gitlab-ci.yml
phpunit:
parallel: 8
script:
- vendor/bin/testflake vendor/bin/phpunit

The first run has no timing data and falls back to an alphabetical split. From the second run on, shards are balanced by recorded duration.

Every shard of one run has to agree on a run id, or each becomes its own pipeline holding the whole suite. On GitLab that is automatic; elsewhere you pass it.

Complete pipelines for GitLab CI, GitHub Actions, CircleCI, Jenkins and Travis are in Continuous integration.

Ask PHPUnit for a Cobertura report and the client uploads it, so each run has a Coverage tab listing every file with the share of its executable lines that ran:

Terminal window
vendor/bin/testflake vendor/bin/phpunit --coverage-cobertura=coverage.xml

A <cobertura outputFile="..."/> entry in your phpunit.xml works just as well — the client reads either. Coverage is optional throughout: a run without it is an ordinary run, and a failed upload is a warning and nothing more.

Every shard uploads what its own tests loaded and the server adds them together, so the figures are the whole run’s rather than one machine’s.

By default TestFlake stores the numbers and not the files. Add --include-source-files and the client uploads the source of each covered file too, which is what lets the dashboard show it annotated line by line:

Terminal window
vendor/bin/testflake --include-source-files vendor/bin/phpunit \
--coverage-cobertura=coverage.xml

Files are stored per project and keyed by their contents, so a file is uploaded once and never again until it changes. The first run of a suite sends the checkout; every run after it sends only what the commit touched.

Three things are never sent: files over 1 MiB, files that are not valid UTF-8, and anything the client cannot read. Nothing else about the flag is configurable — leave it off and no source ever leaves your CI machine.

The client uses three flags PHPUnit already has, which is why there is nothing to install into your test runner:

Flag Used for
--list-tests-xml Enumerating the suite, with your own flags applied.
--test-id-filter-file Selecting exactly this shard’s tests.
--log-junit Reading back what each test did and how long it took.

--test-id-filter-file is only in recent PHPUnit releases. The client checks for it and falls back to a --filter regex on older versions — no configuration needed either way.

The wrapper’s own, consumed before the PHPUnit command it forwards:

Flag Default Purpose
--shard=N/M detected on GitLab Which shard this machine is.
--include-source-files off Uploads the source of each covered file.
Variable Default Purpose
TESTFLAKE_KEY — Attributes runs to a project. Treat as a credential.
TESTFLAKE_HOST — API base URL.
TESTFLAKE_RUN_ID detected on GitLab and GitHub Ties the shards of one run together.
TESTFLAKE_BRANCH detected on GitLab and GitHub Branch the run belongs to.