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)
 ])