2014-04-08 13 views
10

Non riesco a capire perché sto ottenendo questo errore durante questo test. Sembra che il mio test corrisponda esattamente al resto del codice. Cosa sto trascurando?Mance nessun gestore corrispondente per la chiusura

Nella mia prova ho:

$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']); 
      }) 
     ); 

L'errore:

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 (
    ), 
), 
)) 

Parte della mia mancanza di comprensione può avere a che fare con il fatto che io non sono sicuro di quello che il Objects: array(....) è che appare in fondo all'errore.

risposta

34

TL; DR: il vostro argomento di chiusura per Mockery::on ha bisogno di tornare true o false.

La spiegazione più:

Il problema è con il tuo invito Mockery::on. Questo metodo richiede una chiusura (o un'altra funzione) come argomento. Tale chiusura dovrebbe restituire vero o falso, a seconda che l'argomento della chiusura soddisfi il test.

E 'stata una spiegazione piuttosto confusa, quindi cercherò un esempio :-)

Si consideri il seguente aspettativa:

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

Questa aspettativa saranno soddisfatte se il sistema in prova (SUT) chiama

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

e il valore di $x sarà "qualcosa".

Ora gli sviluppatori di Mockery hanno capito che ci sono alcune aspettative che semplicemente non possono soddisfare. Per esempio (e questo è qualcosa che mi ha fatto inciampare per un po '), una chiusura. Si scopre che una chiusura in PHP è una specie di complicata risorsa interna, e anche se si definiscono due chiusure in modo identico, non saranno uguali. Considerare:

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

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

echo il valore "False". Perché? Dalla mia comprensione limitata dell'argomento, ha qualcosa a che fare con la rappresentazione interna degli oggetti di chiusura in PHP. Quindi, quando stai prendendo in giro un metodo che richiede una chiusura come argomento, , non c'è modo di soddisfare l'aspettativa.

Il metodo Mockery::on() fornisce un modo per aggirare questo problema. Utilizzando questo metodo, puoi passare una chiusura (diversa) a Mockery che restituisce true o false, a seconda che i test dei tuoi test dimostrino che hai gli argomenti giusti. Un esempio:

Immaginate che myclass::mymethod richieda una chiusura come argomento. Quanto segue sempre esito negativo, indipendentemente da ciò che la chiusura si passa al mymethod nel SUT:

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

Questo perché derisione confronterà l'argomento passato nel SUT (una chiusura) per la chiusura sopra definito (function($v){ echo $v; }) e quel test fallirà, anche se le due chiusure sono identicamente definite.

Utilizzando Mockery::on(), è possibile riscrivere il test come segue:

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

Ora, quando derisione valuta l'aspettativa, chiamerà la chiusura passata come argomento a Mockery::on(). Se restituisce true, Mockery considererà le aspettative passate; se restituisce false, Mockery lo considererà come non riuscito.

L'aspettativa in questo esempio passerà per qualsiasi chiusura passata a myclass::mymethod, che probabilmente non è abbastanza specifica. Probabilmente vuoi un test più sofisticato, ma questa è l'idea di base.

+0

/facepalm .... So che è necessario restituire vero o falso e completamente escluso. Hai completamente ragione! Grazie per l'esplosione dettagliata, +1! – Webnet

+0

Sono stato sconcertato da questo stesso problema più volte nelle ultime settimane. Penso che lo anniderò, quindi non devo continuare a risolverlo anch'io :-) – Kryten

+0

Perché questo ha solo 3 upvotes? – DanSingerman

Problemi correlati