Это появилось, пытаясь написать тест для метода класса, который вызывает макет с закрытием. Как бы вы подтвердили, что закрытие называется?
Я знаю, что вы сможете утверждать, что параметр является экземпляром Closure
. Но как бы вы могли проверить что-нибудь о закрытии?
Например, как бы вы проверили переданную функцию:
class SUT { public function foo($bar) { $someFunction = function() { echo "I am an anonymous function"; }; $bar->baz($someFunction); } } class SUTTest extends PHPUnit_Framework_TestCase { public function testFoo() { $mockBar = $this->getMockBuilder('Bar') ->setMethods(array('baz')) ->getMock(); $mockBar->expects($this->once()) ->method('baz') ->with( /** WHAT WOULD I ASSERT HERE? **/); $sut = new SUT(); $sut->foo($mockBar); } }
Вы не можете сравнить два закрытия в PHP. Есть ли способ в PHPUnit выполнить параметр, переданный или каким-то образом проверить его?
Ваша проблема заключается в том, что вы не вводите свою зависимость (закрытие), которая всегда усложняет модульное тестирование и делает невозможным изоляцию.
Внесите закрытие в SUT::foo()
а не создавайте его внутри, и вы обнаружите, что тестирование намного проще.
Вот как я бы разработал метод (имея в виду, что я ничего не знаю о вашем реальном коде, поэтому это может быть или не быть практичным для вас):
class SUT { public function foo($bar, $someFunction) { $bar->baz($someFunction); } } class SUTTest extends PHPUnit_Framework_TestCase { public function testFoo() { $someFunction = function() {}; $mockBar = $this->getMockBuilder('Bar') ->setMethods(array('baz')) ->getMock(); $mockBar->expects($this->once()) ->method('baz') ->with($someFunction); $sut = new SUT(); $sut->foo($mockBar, $someFunction); } }
Если у закрытия есть некоторые побочные эффекты внутри SUT
которые могут быть проверены тестом после макетного вызова, используйте returnCallback
чтобы предоставить другое замыкание, которое нужно вызвать с переданными аргументами, и вернуть его возвращаемое значение в SUT
. Это позволит вам вызвать закрытие SUT
, чтобы вызвать побочные эффекты.
class SUT { public function foo($bar) { $someFunction = function() { return 5 * 3; }; return $bar->baz($someFunction); } } class SUTTest extends PHPUnit_Framework_TestCase { public function testFoo() { $mockBar = $this->getMockBuilder('Bar') ->setMethods(array('baz')) ->getMock(); $mockBar->expects($this->once()) ->method('baz') ->will($this->returnCallback(function ($someFunction) { return $someFunction(); })); $sut = new SUT(); self::assertEquals(15, $sut->foo($mockBar)); } }
Если вы хотите __invoke
анонимную функцию (обратный вызов), вы можете __invoke
класс с __invoke
метода __invoke
. Например:
$shouldBeCalled = $this->getMock(\stdClass::class, ['__invoke']); $shouldBeCalled->expects($this->once()) ->method('__invoke'); $someServiceYouAreTesting->testedMethod($shouldBeCalled);
Если вы используете последний PHPUnit, вам придется использовать mock builder, чтобы сделать трюк:
$shouldBeCalled = $this->getMockBuilder(\stdClass::class) ->setMethods(['__invoke']) ->getMock() $shouldBeCalled->expects($this->once()) ->method('__invoke'); $someServiceYouAreTesting->testedMethod($shouldBeCalled);
Вы также можете установить ожидания для аргументов метода или установить возвращаемое значение, точно так же, как и для любого другого метода:
$shouldBeCalled->expects($this->once()) ->method('__invoke') ->with($this->equalTo(5)) ->willReturn(15);