2014-04-08 15 views
10

Ich kann nicht herausfinden, warum ich diesen Fehler während dieses Tests bekomme. Mein Test scheint genau mit dem Rest des Codes übereinzustimmen. Was übersehe ich?Spott keine passende Handler für Schließung

In meinem Test habe ich:

$passwordBroker = m::mock('Illuminate\Auth\Reminders\PasswordBroker'); 
    $passwordBroker->shouldReceive('reset') 
     ->once() 
     ->with(
      $this->resetAttributes, 
      m::on(function (\Closure $closure) { 
       $this->entity 
        ->shouldReceive('setAttribute') 
        ->once() 
        ->with('password', $this->resetAttributes['password']); 
       $this->entity 
        ->shouldReceive('getAttributes') 
        ->once() 
        ->andReturn($this->resetAttributes); 

       $closure($this->entity, $this->resetAttributes['password']); 
      }) 
     ); 

Der Fehler:

Mockery\Exception\NoMatchingExpectationException: No matching handler found for Mockery_4_Illuminate_Auth_Reminders_PasswordBroker::reset(array('email'=>'[email protected]','password'=>'myTestPassword','password_confirmation'=>'myTestPassword',), Closure). Either the method was unexpected or its arguments matched no expected argument list for this method 

Objects: (array (
    'Closure' => 
    array (
    'class' => 'Closure', 
    'properties' => 
    array (
    ), 
    'getters' => 
    array (
    ), 
), 
)) 

Teil meiner Unverständnis kann mit der Tatsache zu tun, dass ich nicht sicher bin, was die Objects: array(....) ist das erscheint am Ende des Fehlers.

Antwort

34

TL; DR: Ihre Schließung Argument Mockery::on Bedürfnisse true oder false zurückzukehren.

Die längere Erklärung:

Das Problem ist, mit Ihrem Anruf Mockery::on. Diese Methode verwendet eine Schließung (oder eine andere Funktion) als Argument. Je nachdem, ob das Argument für die Schließung den Test erfüllt, sollte diese Schließung wahr oder falsch sein.

Das ist eine ziemlich verwirrende Erklärung war, so werde ich :-)

Betrachten Sie die folgende Erwartung ein Beispiel versuchen:

$mock = Mockery::mock("myclass"); 
$mock->shouldReceive("mymethod") 
    ->with("myargument") 
    ->once() 
    ->andReturn("something"); 

Diese Erwartung, wenn das System im Test erfüllt werden (SUT)

nennt
$x = $myclass->mymethod("myargument"); 

und der Wert von $x wird "etwas".

Jetzt haben die Entwickler von Michery erkannt, dass es einige Erwartungen gibt, die sie einfach nicht erfüllen können. Zum Beispiel (und das ist etwas, das mich für eine Weile stolperte), eine Schließung. Es stellt sich heraus, dass eine Schließung in PHP eine Art komplizierte interne Ressource ist, und selbst wenn Sie zwei Schließungen identisch definieren, werden sie nicht identisch sein. Bedenken Sie:

$x = function($v){ echo $v; }; 
$y = function($v){ echo $v; }; 

echo $x==$y ? "True" : "False"; 

wird den Wert "False" widergeben. Warum? Von meinem begrenzten Verständnis des Themas hat es etwas mit der internen Darstellung von Closure-Objekten in PHP zu tun. Also, wenn Sie eine Methode verspotten, die eine Schließung als Argument erfordert, gibt es keine Möglichkeit, die Erwartung zu erfüllen.

Die Mockery::on() Methode bietet einen Weg um dies. Mit dieser Methode können Sie eine (andere) Sperrung an Mockery übergeben, die als wahr oder falsch bewertet wird, je nachdem, ob Ihre Tests zeigen, dass Sie die richtigen Argumente haben. Ein Beispiel:

Stellen Sie sich vor, dass eine Schließung als Argument erfordert. Im Folgenden wird immer scheitern, unabhängig davon, was Schließung Sie im SUT zu mymethod passieren:

$mock = Mockery::mock("myclass"); 
$mock->shouldReceive("mymethod") 
    ->with(function($v){ echo $v; }) 
    ->once() 
    ->andReturn("something"); 

Dies liegt daran, Mockery wird das Argument in dem SUT (a Verschluss) auf die oben (function($v){ echo $v; }) definiert Schließung geführt vergleichen und Dieser Test wird fehlschlagen, auch wenn die beiden Schließungen identisch definiert sind.

Mit Mockery::on(), können Sie den Test wie folgt umschreiben:

$mock = Mockery::mock("myclass"); 
$mock->shouldReceive("mymethod") 
    ->with(Mockery::on(function($value){ 
     return is_callable($value); 
    }) 
    ->once() 
    ->andReturn("something"); 

Nun, wenn Mockery die Erwartung auswertet, wird die Schließung als Argument an Mockery::on() geben nennen. Wenn es true zurückgibt, wird Motelery die erwartete Erwartung berücksichtigen; Wenn es false zurückgibt, wird Mockery es als gescheitert betrachten.

Die Erwartung in diesem Beispiel wird für jede Schließung übergeben, die an myclass::mymethod übergeben wird, die wahrscheinlich nicht spezifisch genug ist. Sie möchten wahrscheinlich einen anspruchsvolleren Test, aber das ist die Grundidee.

+0

/facepalm .... Ich weiß, es muss wahr oder falsch zurück und vollständig ausgeschlossen werden. Du hast vollkommen recht! Danke für die ausführliche Erklärung, +1! – Webnet

+0

Ich wurde in den letzten Wochen mehrmals von demselben Problem überrascht. Ich denke, ich werde das Lesezeichen setzen, damit ich es nicht immer wieder lösen muss :-) – Kryten

+0

Warum hat das nur 3 upvotes? – DanSingerman

Verwandte Themen