Teaches Psalm the conventions of PHPUnit, so analysing your test suite stops producing false positives.
Psalm 7: nothing to install. The plugin ships with Psalm and is enabled automatically when your composer.json requires phpunit/phpunit (see loadLibraryPlugins).
Psalm 6 and older:
composer require --dev psalm/plugin-phpunit
vendor/bin/psalm-plugin enable psalm/plugin-phpunitPlugin issues are suppressed by name: <PluginIssue name="MissingParentCall" errorLevel="suppress"/>.
Providers (#[DataProvider], #[DataProviderExternal], #[DataProviderClosure], @dataProvider) and their datasets are checked against the test method. A provider that is not public static is an InvalidDataProvider. Named datasets are matched to parameters by name (InvalidNamedArgument, TooFewArguments). Parameters take the types of the datasets.
public static function amounts(): iterable
{
yield [100];
yield ['100'];
}
#[DataProvider('amounts')] // InvalidArgument
public function testAmount(int $cents): void {}
#[DataProvider('amunts')] // UndefinedMethod
public function testTypo(int $cents): void {}Tests, providers and TestCase classes are not unused code. Properties set in setUp() or a #[Before] method are initialized (no MissingConstructor). Exceptions declared by PHPUnit's API are not MissingThrowsDocblock.
$this->expectException(InvalidArgumentException::class);
strtoupper($name); // no UnusedFunctionCallClasses, methods and functions in coverage attributes (and @covers on PHPUnit 11 and older) must exist.
#[CoversMethod(Invoice::class, 'totl')] // UndefinedMethod
#[CoversFunction('format_money')] // UndefinedFunctionAssertions that a dedicated one expresses better are reported as UnspecificNullAssertion, UnspecificBooleanAssertion, UnspecificCountAssertion and UnspecificEqualsAssertion. They are errors at errorLevel="1" and info at higher levels.
self::assertSame(null, $maybe); // UnspecificNullAssertion
self::assertSame(true, $flag); // UnspecificBooleanAssertion
self::assertSame(3, count($items)); // UnspecificCountAssertion
self::assertEquals(1, $int); // UnspecificEqualsAssertionPsalter fixes the null, boolean and equals ones.
vendor/bin/psalter --issues=UnspecificNullAssertion,UnspecificBooleanAssertion,UnspecificEqualsAssertionMethods configured on a mock or stub must exist on the mocked class. Intersection doubles and Prophecy calls get their proper types.
$mailer = $this->createMock(Mailer::class);
$mailer->method('sendd'); // UndefinedMethodPHPUnit 12 and newer ignore docblock metadata, so it is an IgnoredDocblockMetadata. On older versions, annotations PHPUnit cannot parse (@dataProvider with no value) are an InvalidPhpUnitAnnotation.
/** @test */ // IgnoredDocblockMetadata
public function itWorks(): void {}When a base test case declares setUp(), tearDown() or another fixture hook, overrides must call parent::, or they are a MissingParentCall.
final class OrderTest extends DatabaseTestCase
{
protected function setUp(): void {} // MissingParentCall
}assertNotEmpty() narrows its argument (?Customer becomes Customer).
self::assertNotEmpty($customer);
$customer->name(); // no issueMIT