In this article we will have a brief look into the latest update to phpstan/phpstan-phpunit 2.0.8.

PHPStan validates PHPUnit data-providers

One of the features, which I am most proud of is the data-provider validation. It was requested by several people years ago, but we did not yet have a good idea how to make it happen without major changes in the PHPStan core.

Starting with this release, we take each data-set of a data-provider and check it against the signature of the corresponding test-case.

At the time of writing we support multiple kinds of data-providers:

  • @test
  • #[Test]
  • “test*” method name prefix
  • @dataProvider
  • #[DataProvider]
  • static data-provider
  • non-static data-provider
  • return [] data-providers
  • yield [] data-providers
  • yield from [] data-providers
  • named arguments in data-providers

See it in action:

#[DataProvider('aProvider')]
public function testTrim(string $expectedResult, string $input): void
{
}

public function aProvider(): array
{
   return [
      [
         'Hello World',
         " Hello World \n",
      ],
      [
         // Parameter #2 $input of method FooTest::testTrim() expects string, int given.
         'Hello World',
         123,
      ],
      [
         // Parameter #2 $input of method FooTest::testTrim() expects string, false given.
         'Hello World',
         false,
      ],
      [
         // Method FooTest::testTrim() invoked with 1 parameter, 2 required.
         'Hello World',
      ],
   ];
}

For this to happen we re-use existing rules for method call validation via the newly introduced NodeCallbackInvoker. This new interface allows us to create virtual made-up AST nodes, and handle them like regular method calls.

Related pull requests:

Ignore missingType.iterableValue for data-providers

You likely have been haunted by this error in your test-suite:

Method DataProviderIterableValueTest\Foo::dataProvider() return type has no value type specified in iterable type iterable.

Even in the PHPStan-src codebase this error was ignored by NEON config in the past, as it was really not that useful to repeat all types in every data-provider, which were already present in the test-case method signatures.

As you already saw in the above paragraph we learned how to validate data-providers with this release. We went one step further and re-used the existing validation logic to omit the missingType.iterableValue error only for those data-providers which we are able to validate. This is possible by implementing a new IgnoreErrorExtension.

Improved assertArrayHasKey() inference

Based on a fix in the PHPStan core, we are now able to properly narrow types after a call to PHPUnits’ assertArrayHasKey(). This will help to prevent false positive errors you may have experienced in the past.

PHPUnit version detector

With the addition of PHPUnitVersionDetector we will be able to easily implement rules or extensions tailored to certain PHPUnit versions.

This will be useful in the future, so we can for example assist in PHPUnit migrations and pave the way for a smoother upgrade path.

Performance improvements

People reading my blog or social media posts know my obsession in making things faster. This release is no difference, as some changes have been done to make most PHPUnit specific rules more efficient by reducing unnecessary work.

Easter eggs included

There is even more magic under the hood.

We have a experimental feature in PHPStan which allows us to not just report errors, but also --fix some of them. This new ability was also added to a few assert* rules.

For example, we can turn $this->assertSame(true, $this->returnBool()); into $this->assertTrue($this->returnBool());.

Summary

I spent a lot of time over a few weeks to make the PHPUnit integration shine. We are on a totally new level and even more new cool stuff is getting possible.

Make sure your boss considers sponsoring my open source work, so I can spend more time on your beloved code quality tooling.

Found a bug? Please help improve this article.


<
Previous Post
Mutation testing with Infection in PHP
>
Blog Archive
Archive of all previous blog posts