PHPUnit test con chiusure

Questo è venuto fuori cercando di scrivere un test per un metodo di una class che chiama un metodo di simulazione con una chiusura. Come verificherai la chiusura della chiamata?

So che saresti in grado di affermare che il parametro è un'istanza di Closure . Ma come controlleresti qualcosa sulla chiusura?

Ad esempio, come verificherai la function che viene passata:

  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(arrays('baz')) ->getMock(); $mockBar->expects($this->once()) ->method('baz') ->with( /** WHAT WOULD I ASSERT HERE? **/); $sut = new SUT(); $sut->foo($mockBar); } } 

Non puoi confrontare due chiusure in PHP. C'è un modo in PHPUnit per eseguire il parametro passato o in qualche modo verificarlo?

Il tuo problema è che non stai iniettando la tua dipendenza (la chiusura), che rende sempre più difficile testare l'unità e può rendere imansible l'isolamento.

Inietti la chiusura in SUT::foo() invece di crearla lì dentro e troverai molto più facile il test.

Ecco come progetterei il metodo (tenendo presente che non so nulla del tuo codice reale, quindi questo può o non può essere pratico per te):

 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(arrays('baz')) ->getMock(); $mockBar->expects($this->once()) ->method('baz') ->with($someFunction); $sut = new SUT(); $sut->foo($mockBar, $someFunction); } } 

Se la chiusura presenta alcuni effetti collaterali all'interno di SUT che potrebbero essere verificati dal test dopo la chiamata returnCallback , utilizzare returnCallback per fornire un'altra chiusura da call con gli argomenti passati e restituire il valore restituito a SUT . Questo ti permetterà di call la chiusura di SUT per causare gli effetti collaterali.

  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(arrays('baz')) ->getMock(); $mockBar->expects($this->once()) ->method('baz') ->will($this->returnCallback(function ($someFunction) { return $someFunction(); })); $sut = new SUT(); self::assertEquals(15, $sut->foo($mockBar)); } } 

Se vuoi prendere in giro una function anonima (callback) puoi prendere in giro una class con il metodo __invoke . Per esempio:

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

Se stai usando l'ultima versione di PHPUnit, dovresti usare mock builder per fare il trucco:

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

Puoi anche impostare le aspettative per gli argomenti del metodo o impostare un valore di return, proprio come faresti per qualsiasi altro metodo:

 $shouldBeCalled->expects($this->once()) ->method('__invoke') ->with($this->equalTo(5)) ->willReturn(15);