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
You can use \Magento\TestFramework\ObjectManager::addSharedInstance for this.
Example:
$objectManager->addSharedInstance($mock, TheInterface::class);
- 
 3Works well if there is no preference set yet indi.xml, but the preference seems to have priority over the shared instance.Fabian Schmengler– Fabian Schmengler2016年03月06日 19:01:25 +00:00Commented Mar 6, 2016 at 19:01
- 
 4In 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– KAndy2016年03月06日 19:36:57 +00:00Commented Mar 6, 2016 at 19:36
- 
 1Nice, that worked!Fabian Schmengler– Fabian Schmengler2016年03月06日 19:46:06 +00:00Commented Mar 6, 2016 at 19:46
- 
 3Please 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 :-PGiel Berkers– Giel Berkers2019年01月28日 11:13:26 +00:00Commented Jan 28, 2019 at 11:13
- 
 @GielBerkers You saved me an hour :-)Michał Biarda– Michał Biarda2024年06月14日 12:45:34 +00:00Commented Jun 14, 2024 at 12:45
Explore related questions
See similar questions with these tags.