使用闭包进行PHPUnit测试

Sch*_*eis 15 php phpunit closures

这就试图为一个类的方法编写一个测试,该类调用带闭包的mock方法.你如何验证被调用的闭包?

我知道你可以断言参数是一个实例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);
     }
 }
Run Code Online (Sandbox Code Playgroud)

你无法比较PHP中的两个闭包.PHPUnit中有没有办法执行传入的参数或以某种方式验证它?

Ser*_*nyy 14

如果你想模拟一个匿名函数(回调),你可以用__invoke方法模拟一个类.例如:

$shouldBeCalled = $this->getMock(\stdClass::class, ['__invoke']);
$shouldBeCalled->expects($this->once())
    ->method('__invoke');

$someServiceYouAreTesting->testedMethod($shouldBeCalled);
Run Code Online (Sandbox Code Playgroud)

如果您使用的是最新的PHPUnit,则必须使用模拟构建器来执行此操作:

$shouldBeCalled = $this->getMockBuilder(\stdClass::class)
    ->setMethods(['__invoke'])
    ->getMock();

$shouldBeCalled->expects($this->once())
    ->method('__invoke');

$someServiceYouAreTesting->testedMethod($shouldBeCalled);
Run Code Online (Sandbox Code Playgroud)

您还可以设置方法参数的期望值或设置返回值,就像对任何其他方法一样:

$shouldBeCalled->expects($this->once())
    ->method('__invoke')
    ->with($this->equalTo(5))
    ->willReturn(15);
Run Code Online (Sandbox Code Playgroud)


FtD*_*Xw6 8

你的问题是你没有注入你的依赖(闭包),这总是使单元测试变得更难,并且可以使隔离变得不可能.

注入封闭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);
    }
}
Run Code Online (Sandbox Code Playgroud)


Dav*_*ess 6

如果闭包有一些副作用SUT,可以通过模拟调用后的测试来验证,则使用returnCallback提供另一个闭包来调用传递的参数并返回其返回值SUT.这将允许你调用SUT's closure来引起副作用.

 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));
     }
 }
Run Code Online (Sandbox Code Playgroud)