PHPUnit assertion exception caught in code under test, not failing the test
| Q | A |
|---|---|
| PHPUnit version | 12.3.15 |
| PHP version | 8.3.22 |
| Installation Method | PHAR |
Summary
A failing assertion within a ::willReturnCallback(function () { }) triggers the PHPUnit exception to be caught in the code under test, not failing the test itself.
I tried to replicate the code under test here below. Please let me know if you need more clarity on this case, it is quite confusing to me too 😓
I discovered this case while running infection-php: which marked some mutations not being covered, while (from a first sight) they actually are.
Current behavior
A PHPUnit assertion exception is caught, and the test not marked as failed.
How to reproduce
Given the following 2 classes, and the test BarTest.
The test is not failing in the $this->assertSame('foo-foo', $param); as the PHPUnit exception is caught in the Bar::doBar() code instead
<?php
class Foo {
public function doFoo(string $param): void {}
}
class Bar {
public function __construct(private readonly Foo $foo) {}
public function doBar(string $param): void
{
try {
$this->foo->doFoo($param);
} catch (\Exception $e) {
// Looks like the PHPUnit exception is received here
// It is "Can not assert that "foo-foo" is the same as "bar-bar"
// ... but I would expect it to FAIL the test scenario, not to get an exception handled in my code
}
}
}
class BarTest extends \PHPUnit\Framework\TestCase
{
public function testBarDoBar(): void
{
$mockFoo = $this->createMock(Foo::class);
$mockFoo->expects($this->once())
->method('doFoo')
->willReturnCallback(function (string $param): void {
// This is just an example, as the real parameter is a DTO, where we are testing that all properties are
// correct according to some other
$this->assertSame('foo-foo', $param);
});
$bar = new Bar($mockFoo);
$bar->doBar('bar-bar');
}
}
Expected behavior
The test case should FAIL, without the PHPUnit exception being caught in the code under test
The problem here is that the code under test catches all exceptions (catch (Exception $e)), rather than just the ones it is intended to handle. This is almost always a bad idea.
I don't think there's anything that can be done about this in PHPUnit without fundamentally altering how the test doubles runtime works. The fact that this is the first time this has been reported as a problem in almost 20 years of this functionality's existence suggests to me that it is a niche problem which should be documented but not otherwise addressed.