Generate factory or proxy in unit tests: "ReflectionException: Class ...Factory does not exist"
The easiest way to deal with that is to run compilation before running tests:
bin/magento setup:di:compile
The other way is to explicitly define methods for the factory mock eg. instead of doing this:
$someFactoryMock = $this->getMockBuilder('Vendor\Module\Model\SomeFactory')
->disableOriginalConstructor()
->getMock();
Do this:
$someFactoryMock = $this->getMockBuilder('Vendor\Module\Model\SomeFactory')
->disableOriginalConstructor()
->setMethods(['create'])
->getMock();
At some point, I tried to deal with that by calling ObjectManager::getObject
before creating mock, but this doesn't look as a clean solution. Another thing is that it didn't help - it created an object, but did not save class in var/generation. I haven't dig into this more.
The problem originates from PHPUnit mocking library, as it cannot autoload the needed class.
If you take a look into Magento dev repo, it setups Autoloader catcher, that generates a class when it is requested. If you create similar bootstrap file in your module repository it will work quite well: https://github.com/magento/magento2/blob/develop/dev/tests/unit/framework/autoload.php
<?php
/**
* Copyright © 2016 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
$autoloader = new \Magento\Framework\TestFramework\Unit\Autoloader\ExtensionGeneratorAutoloader(
new \Magento\Framework\Code\Generator\Io(
new \Magento\Framework\Filesystem\Driver\File(),
TESTS_TEMP_DIR . '/var/generation'
)
);
spl_autoload_register([$autoloader, 'load']);
However I would advise using a different approach, by utilizing a virtual file system, so your materialized generated classes won't break your build if generated classes interface signature changes.
composer require --dev mikey179/vfsStream
And then in your bootstrap file:
$autoloader = new \Magento\Framework\TestFramework\Unit\Autoloader\ExtensionGeneratorAutoloader(
new \Magento\Framework\Code\Generator\Io(
new \Magento\Framework\Filesystem\Driver\File(),
org\bovigo\vfs\vfsStream::setup('my_generated_classes')->url()
)
);
spl_autoload_register([$autoloader, 'load']);
I was using similar approach when created an adapter for PHPSpec https://github.com/EcomDev/phpspec-magento-di-adapter/blob/master/src/Extension.php#L98
Also you may use something like this
private function getMockupFactory($instanceName)
{
/** Magento\Framework\TestFramework\Unit\Helper\ObjectManager */
$objectManager = $this->objectManagerHelper;
$factory = $this->getMockBuilder($instanceName . 'Factory')
->disableOriginalConstructor()
->setMethods(['create'])
->getMock();
$factory->expects($this->any())
->method('create')
->will($this->returnCallback(function($args) use ($instanceName, $objectManager) {
return $objectManager->getObject($instanceName, $args);
}));
return $factory;
}
and someWhere in code just pass
class Some {
__constructor(
MyFactory $myFactory
){}
}
$this->objectManagerHelper->getObject(Some::class,[
'myFactory' => $this->getMockupFactory(My::class)
])