Codeception / AspectMock

The most powerful and flexible mocking framework for PHPUnit / Codeception.
MIT License
791 stars 128 forks source link

Unable to instrument classes in the /vendor directory #139

Open kzap opened 7 years ago

kzap commented 7 years ago

I was able to get AspectMock to instrument my own classes in my directory but I can not get it to work on classes in the composer /vendor directory....

Is this even possible?

I tried adding vendor to my includePaths but I get the issue in #112

My bootstrap looks basically like this:

require_once __DIR__.'/../vendor/autoload.php'; // composer autoload

$kernel = \AspectMock\Kernel::getInstance();
$kernel->init([
    'debug' => true,
    'appDir' => __DIR__.'/..',
    'cacheDir' => __DIR__.'/_data/aopcache',
    'includePaths' => [
        __DIR__.'/../repo'
    ],
    'excludePaths' => [
        __DIR__,
    ]
]);

Sample of my test code:

    $testDataStr = 'var=1&var=2';
    $paypalIPN = test::double(new \PayPal\IPN\PPIPNMessage($testDataStr), [
        'getRawData' => ['Returned test double'],
    ]);
    $this->assertTrue($paypalIPN->class->hasMethod('getRawData'));
    $testData = $paypalIPN->getRawData();
    $paypalIPN->verifyInvoked('getRawData'); // fails

fails with: Expected PayPal\IPN\PPIPNMessage->getRawData to be invoked but it never occurred. Got:

Versions php 5.6.30 codeception/aspect-mock:2.0.1 @b7f5c50da89419824102400bb4c71988ec33044f goaop/framework:2.1.2 @6e2a0fe13c1943db02a67588cfd27692bddaffa5 goaop/parser-reflection:1.4.0 @adfc38fee63014880932ebcc4810871b8e33edc9

AndrewSologor commented 6 years ago

+1

I want to mock some vendor class, but seems AspectMock is not designed to do this out of the box. Note: my vendor dir has non-standard location.

For now I solved it like this:

Note: mocked vendor classes may throw an exception when they tries to load another classes from the same package.

// make sure your include paths in this array does not end with a slash '/' symbol
$includeDirsFromVendor = [
   ROOT_PATH .  '/library/vendor/someVendorName',
   ROOT_PATH .  '/library/vendor/anotherVendorName/someVendorLib',
];

// add your basic exclude paths to this array
$excludePaths = [
    ROOT_PATH . '/tests',
];

foreach (glob(ROOT_PATH .  '/library/vendor/*', GLOB_ONLYDIR) as $dir) {
    if (!in_array($dir, $includeDirsFromVendor)) {
        foreach (glob($dir . '/*', GLOB_ONLYDIR) as $subDir) {
            if (!in_array($subDir, $includeDirsFromVendor)) {
                $excludePaths[] = $subDir;
            }
        }
    }
}

After this $excludePaths stores all vendor dirs + subdirs except those which listed in $includeDirsFromVendor.

$kernel = \AspectMock\Kernel::getInstance();
$kernel->init([
    'debug' => true,
    'cacheDir' => ROOT_PATH . '/tests/_data/cache',
    'includePaths' => [
        ROOT_PATH,
    ],
    'excludePaths' => $excludePaths,
]);

And it works, at least for a library that I've mocked. But I have feeling that it's better to refactor my code and do not mock vendor classes.

kzap commented 6 years ago

Nice solution. I assume this was done so the code being run does not mock itself

btw tested out the above code and it works well!