antecedent / patchwork

Method redefinition (monkey-patching) functionality for PHP.
https://antecedent.github.io/patchwork/
MIT License
444 stars 40 forks source link

Incompatibility with Mockery #99

Closed matzeeable closed 4 years ago

matzeeable commented 4 years ago

Hi!

I am using PHPUnit 7 and want to unit test a WordPress plugin (I can not go with PHPUnit 8 because WordPress has official an incompatibility, see here). Additionally I use mockery as mock framework instead of PHPUnit's mock builder.

I have the following strange situation, my test file MyAwesomeTest.php looks like this

<?php
declare(strict_types=1);

use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
use Mockery\MockInterface;
use PHPUnit\Framework\TestCase;

// Bootstrap composer
require_once __DIR__ . '/../../vendor/antecedent/patchwork/Patchwork.php';
require_once __DIR__ . '/../../vendor/autoload.php';

trait Foo {
    public function foo() {
        return $this->anotherFoo();
    }

    public function anotherFoo() {
        return 'my value';
    }
}

class Bar {
    use Foo;
}

class MyAwesomeTest extends TestCase {
    use MockeryPHPUnitIntegration;

    public function testFoo() {
        /** @var MockInterface|Bar */
        $mock = Mockery::mock(Bar::class);
        $mock
            ->makePartial()
            ->shouldReceive('anotherFoo')
            ->once()
            ->andReturns('another value');

        $this->assertEquals('another value', $mock->foo());
    }
}

Running the test gives the following result:

matzeeable@MSI:$ vendor/bin/phpunit test/php/MyAwesomeTest.php 
PHPUnit 7.5.18-3-g6aab04011 by Sebastian Bergmann and contributors.

E                                                                   1 / 1 (100%)

Time: 528 ms, Memory: 12.00 MB

There was 1 error:

1) MyAwesomeTest::testFoo
ReflectionException: Trying to invoke non static method Bar::foo() without an object

[...]/vendor/antecedent/patchwork/src/CallRerouting.php:526
[...]/vendor/antecedent/patchwork/src/CallRerouting.php:353
[...]/vendor/antecedent/patchwork/src/CallRerouting.php:535
[...]/vendor/antecedent/patchwork/src/CallRerouting.php:285
[...]/vendor/antecedent/patchwork/src/CallRerouting.php:317
[...]/vendor/antecedent/patchwork/src/Stack.php:27
[...]/vendor/antecedent/patchwork/src/CallRerouting.php:328
[...]/test/php/MyAwesomeTest.php:38

ERRORS!
Tests: 1, Assertions: 0, Errors: 1.

But if I do the following (comment out Patchwork):

// require_once __DIR__ . '/../../vendor/antecedent/patchwork/Patchwork.php';

I get correct results:

matzeeable@MSI:$ vendor/bin/phpunit test/php/MyAwesomeTest.php 
PHPUnit 7.5.18-3-g6aab04011 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 272 ms, Memory: 4.00 MB

OK (1 test, 2 assertions)

Is there any incompatibility with Mockery + Patchwork?

Versions:

Name Version
PHPUnit 7.5.18-3-g6aab04011
PHP 7.2.17-0ubuntu0.18.04.1 (cli) (built: Apr 18 2019 14:12:38) ( NTS )
Mockery dev-master
Patchwork 2.1.11

Regards, Matthew 🙂

antecedent commented 4 years ago

Hi, do you happen to have a patchwork.json in your setup by any chance?

matzeeable commented 4 years ago

Thanks for your fast response! Yeah, my patchwork.json looks like this:

{
    "redefinable-internals": ["call_user_func", "error_log"]
}

I just figured out if I remove the internals and make the packwork.json like the below, the test passes:

{
    "redefinable-internals": []
}

Do you need anything else to know? Can you reprocedure the problem? What versions of the tools are you using?

antecedent commented 4 years ago

I reproduced it with Mockery 1.3.0 and PHPUnit 8.5.0. A fix is now in the master branch; please try it.

matzeeable commented 4 years ago

I implemented the fix on my local vendor folder and the error is gone, thank you!

Unfortunately I can not rely on the dev-master because I use all the packages together with WP_Mock. When do you release a new version? 🙂

matzeeable commented 4 years ago

Okay, I just found another strange thing I think you can reprocedure... Just try the following test case:

class Service {
    private $foo;

    public function __construct($foo) {
        $this->foo = $foo;
    }
}

trait Foo {
    private $service;

    public function foo() {
        $this->service = new Service($this);
        $this->getService();
        // Do something here with service ...
        return $this->anotherFoo();
    }

    public function getService() {
        return $this->service;
    }

    public function anotherFoo() {
        return 'my value';
    }
}

class Bar {
    use Foo;
}

and the test case keeps the same. Again here, if I do not load patchwork it work as expected. If I have patchwork active, the following error occurs:

1) MyAwesomeTest::testFoo
call_user_func_array() expects parameter 1 to be a valid callback, cannot access parent:: when no class scope is active

XXX/test/php/MyAwesomeTest.php:21
XXX/vendor/antecedent/patchwork/src/CallRerouting.php:529
XXX/vendor/antecedent/patchwork/src/CallRerouting.php:353
XXX/vendor/antecedent/patchwork/src/CallRerouting.php:538
XXX/vendor/antecedent/patchwork/src/CallRerouting.php:285
XXX/vendor/antecedent/patchwork/src/CallRerouting.php:317
XXX/vendor/antecedent/patchwork/src/Stack.php:27
XXX/vendor/antecedent/patchwork/src/CallRerouting.php:328
XXX/test/php/MyAwesomeTest.php:55
antecedent commented 4 years ago

This took considerably longer, but should now be working. Could you confirm that?

I would release a new version shortly thereafter if everything works as intended.

matzeeable commented 4 years ago

It works as expected! Should I let you know if I find further compatibility issues with patchwork + mockery? 🙂

antecedent commented 4 years ago

Great! The fixes are now in version 2.1.12.

Should I let you know if I find further compatibility issues with patchwork + mockery? 🙂

Yes, please do :)