Magento 2.0 – Generate Factory or Proxy in Unit Tests: ‘ReflectionException: Class …Factory Does Not Exist’

code generationfactorymagento-2.0testingunit tests

As far as I understand, Factory and Proxy classes are generated on the fly by the autoloader if they do not exist yet in var/generation (see: What Triggers the Generation of a Factory in Magento 2)

But why do I get this error when referencing a new factory in a unit test?

ReflectionException: Class
Magento\Framework\Api\Search\SearchCriteriaBuilderFactory does not
exist

[…]/vendor/magento/framework/TestFramework/Unit/Helper/ObjectManager.php:161

use Magento\Framework\Api\Search\SearchCriteriaBuilderFactory;
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;

class SearchCriteriaTest extends \PHPUnit_Framework_TestCase
{
    public function testFactoryGeneration()
    {
        $searchCriteriaBuilderFactory = (new ObjectManager($this))->getObject(SearchCriteriaBuilderFactory::class);
    }
}

I am using the bootstrap file dev/tests/unit/framework/bootstrap.php.


Workarounds I found to generate the class:

  • using the real object manager (Thanks @DigitalPianism):

    \Magento\Framework\App\Bootstrap::create(BP, $_SERVER)->getObjectManager()->create('\Magento\Framework\Api\Search\SearchCrite‌​riaBuilderFactory')
    
  • run setup:di:compile (given the factory is referenced in a constructor)

But I still hope to find a clean and performant solution.

Also, not sure if related, but create() of the generated factory from the unit test object manager returns null, so I don't even have a working factory yet.

Best Answer

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.