| Q |
A |
| PHPUnit version |
11.5.18 |
| PHP version |
8.3.20 |
| Installation Method |
Composer |
| Phpstan version |
2.1.13 |
| Laravel version |
12.10.2 |
Summary
The phpunit asserts assertEmpty and assertNotEmpty are designed to work correctly on any object that implements the Countable trait and they do exactly that. isEmpty::matches checks to see if the passed parameter implements Countable and, if so, returns return count($other) === 0;.
However unit tests that take advantage of this behaviour/feature will fail phpstan's static analysis since the assertEmpty and assertNotEmpty methods include the phpstan directives
@phpstan-assert empty $actual
and
@phpstan-assert !empty $actual
respectively.
So unit tests that work correctly, fail phpstan
Current behavior
Unit tests behave correctly, but the code fails phpstan with
Call to method PHPUnit\Framework\Assert::assertEmpty() with
Illuminate\Support\Collection<(int|string), mixed> will always evaluate to false.
🪪 method.impossibleType
💡 Because the type is coming from a PHPDoc, you can turn off this check by setting
How to reproduce
This tiny sample unit test, AssertOnCollectionsTest, creates both empty and non-empty Laravel collections, which implement \Countable, and checks them with assertEmpty and assertNotEmpty.
<?php
namespace Tests;
use Illuminate\Support\Collection;
class AssertOnCollectionsTest extends \Illuminate\Foundation\Testing\TestCase
{
// Should Pass
public function testAssertEmptyOnEmpty()
{
$emptyCollection = collect();
$this->assertEmpty($emptyCollection);
}
// Should Fail
public function testAssertNotEmptyOnEmpty()
{
$emptyCollection = collect();
$this->assertNotEmpty($emptyCollection);
}
// Should Fail
public function testAssertEmptyOnNotEmpty()
{
$nonEmptyCollection = collect([1, 2, 3]);
$this->assertEmpty($nonEmptyCollection);
}
// Should Pass
public function testAssertNotEmptyOnNotEmpty()
{
$nonEmptyCollection = collect([1, 2, 3]);
$this->assertNotEmpty($nonEmptyCollection);
}
}
Running phpunit on this test results in the expected behaviour for the 4 tests: Pass, Fail, Fail, Pass
$ vendor/bin/phpunit tests/AssertOnCollectionsTest.php
PHPUnit 11.5.18 by Sebastian Bergmann and contributors.
Runtime: PHP 8.3.20
.FF. 4 / 4 (100%)
Time: 00:00.642, Memory: 24.00 MB
There were 2 failures:
1) Tests\AssertOnCollectionsTest::testAssertNotEmptyOnEmpty
Failed asserting that an object is not empty.
/Users/lphilps/Sites/WhoPlusYou-Html5/tests/AssertOnCollectionsTest.php:22
2) Tests\AssertOnCollectionsTest::testAssertEmptyOnNotEmpty
Failed asserting that an object is empty.
/Users/lphilps/Sites/WhoPlusYou-Html5/tests/AssertOnCollectionsTest.php:30
FAILURES!
Tests: 4, Assertions: 4, Failures: 2.
That's good, but phpstan on the file produces errors:
$ vendor/bin/phpstan analyze tests/AssertOnCollectionsTest.php
Note: Using configuration file .../phpstan.neon.dist.
1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%
------ ----------------------------------------------------------------------------------------------------------------
Line AssertOnCollectionsTest.php
------ ----------------------------------------------------------------------------------------------------------------
12 Call to method PHPUnit\Framework\Assert::assertEmpty() with Illuminate\Support\Collection<(int|string), mixed>
will always evaluate to false.
🪪 method.impossibleType
💡 Because the type is coming from a PHPDoc, you can turn off this check by setting
treatPhpDocTypesAsCertain: false in your phpstan.neon.dist.
20 Call to method PHPUnit\Framework\Assert::assertNotEmpty() with Illuminate\Support\Collection<(int|string),
mixed> will always evaluate to true.
🪪 method.alreadyNarrowedType
💡 Because the type is coming from a PHPDoc, you can turn off this check by setting
treatPhpDocTypesAsCertain: false in your phpstan.neon.dist.
28 Call to method PHPUnit\Framework\Assert::assertEmpty() with Illuminate\Support\Collection<int, int> will
always evaluate to false.
🪪 method.impossibleType
💡 Because the type is coming from a PHPDoc, you can turn off this check by setting
treatPhpDocTypesAsCertain: false in your phpstan.neon.dist.
36 Call to method PHPUnit\Framework\Assert::assertNotEmpty() with Illuminate\Support\Collection<int, int> will
always evaluate to true.
🪪 method.alreadyNarrowedType
💡 Because the type is coming from a PHPDoc, you can turn off this check by setting
treatPhpDocTypesAsCertain: false in your phpstan.neon.dist.
------ ----------------------------------------------------------------------------------------------------------------
[ERROR] Found 4 errors
Expected behavior
Phpstan should pass cleanly on this unit test.
Summary
The phpunit asserts
assertEmptyandassertNotEmptyare designed to work correctly on any object that implements theCountabletrait and they do exactly that.isEmpty::matcheschecks to see if the passed parameter implementsCountableand, if so, returnsreturn count($other) === 0;.However unit tests that take advantage of this behaviour/feature will fail phpstan's static analysis since the
assertEmptyandassertNotEmptymethods include the phpstan directivesand
respectively.
So unit tests that work correctly, fail phpstan
Current behavior
Unit tests behave correctly, but the code fails phpstan with
How to reproduce
This tiny sample unit test,
AssertOnCollectionsTest, creates both empty and non-empty Laravel collections, which implement\Countable, and checks them withassertEmptyandassertNotEmpty.Running phpunit on this test results in the expected behaviour for the 4 tests: Pass, Fail, Fail, Pass
That's good, but phpstan on the file produces errors:
Expected behavior
Phpstan should pass cleanly on this unit test.