Skip to content
Latchkey

PHPUnit "FAILURES! Tests: N, Assertions: M, Failures: K" in CI

PHPUnit ran your tests and one or more assertions did not hold, so it exits non-zero and fails the job. Either the code is genuinely wrong, or the test depends on something that differs between local and CI - timezone, locale, ordering, or missing config.

What this error means

PHPUnit prints "FAILURES!" with a summary like "Tests: 42, Assertions: 118, Failures: 3" and details each "Failed asserting that ...". The step exits 1 (or 2 with errors).

PHPUnit output
1) App\Tests\InvoiceTest::testTotal
Failed asserting that 100.0 matches expected 99.99.

/app/tests/InvoiceTest.php:31

FAILURES!
Tests: 42, Assertions: 118, Failures: 1.

Common causes

A genuine assertion failure

The code produces a value the test does not expect. The "Failed asserting that X matches expected Y" line shows the exact mismatch.

CI-only environment differences

Tests that depend on timezone, locale, current date, random ordering, or an env var/config present locally but not in CI fail only on the runner.

How to fix it

Read the diff and fix code or expectation

Use the failure detail to decide whether the code or the assertion is wrong, and run just that test while iterating.

Terminal
vendor/bin/phpunit --filter testTotal --testdox

Make the environment deterministic

  1. Pin timezone and locale (date.timezone, LC_ALL) so CI matches local.
  2. Provide every env var/config the tests need in the CI job (e.g. a .env.testing).
  3. Freeze "now" with a clock abstraction instead of new DateTime() in assertions.

How to prevent it

  • Set timezone/locale explicitly in phpunit.xml or the CI env.
  • Avoid real time, randomness, and network in unit tests; inject them.
  • Run the suite locally with the CI PHP version before pushing.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →