zenstruck / foundry

A model factory library for creating expressive, auto-completable, on-demand dev/test fixtures with Symfony and Doctrine.
https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html
MIT License
608 stars 63 forks source link

[2.x] Deprecations / Upgrading w/o Symfony's Deprecation Helper #579

Closed jrushlow closed 1 week ago

jrushlow commented 3 months ago

I was just skimming through the UPGRADE docs to start testing the 2.x branch in an internal app I'm working on and noticed:

https://github.com/zenstruck/foundry/blob/bbcef6a15e042895c131d045c52d784efc540b71/UPGRADE-2.0.md?plain=1#L20

It would be nice if we did not have to rely on the deprecation helper from phpunit-bridge as part of the upgrade process. e.g. I'm using PHPUnit 11 and the bridge is not available to me yet.

I haven't looked into too deeply yet (this may not even be needed / feasible), but I'm wondering if it is possible to leverage PHPUnit 10/11's event system to trigger deprecation notices in 1.x (for those of us that are not using PHPUnit 9.x).

Thoughts?

nikophil commented 3 months ago

Hi @jrushlow

I spent a large part of the day yesterday figuring out why the deprecations were not bubbling in PHPUnit >= 10. There are two main problems: as you've noticed in https://github.com/zenstruck/foundry/pull/577, there is a whole subject around the error handler, which I kinda mitigated with this PR.

Another annoyance is that PHPUnit does not handle deprecations when they occur in a data provider whereas Foundry is used a lot in data providers in userland.

Maybe we can emit ourself the deprecation events instead of calling trigger_deprecation() when PHPUnit >= 10 is used?

This will need to dig a little bit more, as I still have no experience in recent PHPUnit versions nor in its event system, but that would be a good start!

Depending on how this is feasible, we'll change the upgrade guide, in order to explain how to trigger deprecations for both systems.

nikophil commented 3 months ago

Here is how you can trigger deprecation in a test:

public function testSomething()
{
        $emitter = Event\Facade::emitter();

        $emitter->testTriggeredDeprecation(
            $this->valueObjectForEvents(),
            'message',
            'file',
            123,
            suppressed: false,
            ignoredByBaseline: false,
            ignoredByTest: false
        );
}

But this solution is not satisfying for data providers, since it needs an occurrence of a test, and data providers are now static.

A workaround would be to use testRunnerTriggeredDeprecation():

    public static function provide(): iterable
    {
        $emitter = Event\Facade::emitter();
        $emitter->testRunnerTriggeredDeprecation('deprecatoin message');

        yield [/* ... */];
    }

    #[DataProvider('provide')]
    public function testSomething(Factory $factory): void
    {
        // ...
    }

Resulting in the following output:

.                                                                   1 / 1 (100%)

Time: 00:00.033, Memory: 10.00 MB

There was 1 PHPUnit test runner deprecation:

1) deprecation message

Notice that there is no D displayed. I don't know if it is a valid usage of the so called "runner deprecation". All these deprecations are always displayed, disregarding the option --display-deprecations nor --filter, etc... because all data providers are always executed.

Another solution would be to collect all deprecations in data providers, and only trigger them when the test is actually ran. But that sounds like a much more complex solution.

nikophil commented 3 months ago

It seems that PHPUnit will emit deprecations from dataproviders. But this will land in 11.2

nikophil commented 1 week ago

I'm closing this, I did my best for PHPUnit >= 10 to display the maximum of deprecations, all the rest depends on symfony/phpunit