11

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

1 Answer 1

7

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

Example:

$objectManager->addSharedInstance($mock, TheInterface::class);
5
  • 3
    Works well if there is no preference set yet in di.xml, but the preference seems to have priority over the shared instance. Commented Mar 6, 2016 at 19:01
  • 4
    In this case you need add shared interface for preferred class. for ex. if we have <preference for="Psr\Log\LoggerInterface" type="Magento\Framework\Logger\Monolog" /> than you should add shared instance as $objectManager->addSharedInstance($mock, "Magento\Framework\Logger\Monolog");
    – KAndy
    Commented Mar 6, 2016 at 19:36
  • 1
    Nice, that worked! Commented Mar 6, 2016 at 19:46
  • 3
    Please note that if you want to mock a virtual type, you have to use the name of the virtual type, not the class name: $this->objectManager->addSharedInstance($mockedLogger, 'CustomLogger');. Knowing this would have saved me an hour :-P Commented Jan 28, 2019 at 11:13
  • @GielBerkers You saved me an hour :-) Commented Jun 14 at 12:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.