For learning purposes I was trying to write a unit test for one of my Magento 2 around
plugins (interception).
The method I'm testing receives as parameter a \Closure
object that cannot be mocked because \Closure
is a final class
How do I proceed in this case? Create an actual closure in my test class and pass that as parameter? Or any other way?
Let's say my method looks like this. Ignore the content, focus on the concept.
class Something
{
public function aroundDoStuff(SomeModel $subject \Closure $closure, $extraParam)
{
if ($subject->getSomeValue() == 1) {
return 'Custom return';
}
return $closure($extraParam);
}
}
Now for my test, I mocked the first parameter SomeModel
.
$subject = $this->getMock(SomeModel::class, [], [], '', false);
$subject->expects($this->any())->method('getSomeValue')->will($this->returnValue('1'));
$closure = ????; // WHAT GOES HERE
$obj = new Something();
$expected = 'Custom return';
$extraParam = 'not important';
$this->assertEquals($expected, $obj->aroundDoStuff($subject, $closure, $extraParam));
Best Answer
To mock callables, I usually mock
__invoke
:The problem is that Magento uses
\Closure
type hints instead ofcallable
. As soon as Magento supports PHP 7.1 you will be able to useClosure::fromCallable($callbackMock)
, until then, wrap it yourself:That aside, I would not bother writing unit tests for plugins most of the time. If there is business logic that I write with unit tests, this would be in another class where the plugin delegates to. To test if the plugin works correctly, an integration test is more appropiate.