Magento2 – Mock Dependencies in Integration Tests

dependency-injectionintegration-testmagento2testing

Suppose the following scenario:

  • I have a class that makes calls to an external service
  • The class implements an interface and is defined as preferred implementation for this interface in di.xml
  • A block receives this interface as constructor parameter
  • I want to test a Magento request in an integration test that uses this block

I don't want to actually call the external service, so I would like to mock that class and wonder what's the best way to do so.

I know that you can define DI preferences on the fly with

$objectManager->configure(
    ['preferences' => [TheInterface::class => MockClass::class]]
);

but this requires defining a mock class MockClass yourself, I cannot use a PHPUnit mock object.

This works okay if the injected class is a factory because I can create a mock factory that creates the actual mock object.

But is this the only way or am I missing something?

Update:

The suggested method

$objectManager->addSharedInstance($mock, TheInterface::class);

looked good first, but only worked as long as there were no preferences defined. These take precedene over shared instances.

I tried to dynamically remove the preference:

$this->objectManager->configure(
    ['preferences' => [TheInterface::class => null]]
);

But unfortunately Magento calls ltrim($to, '\\') on the argument, which converts it to an empty string. This results in:

ReflectionException: Class does not exist

Best Answer

You can use \Magento\TestFramework\ObjectManager::addSharedInstance for this.

Example:

$objectManager->addSharedInstance($mock, TheInterface::class);