2012-11-29 3 views
12

Sto provando a creare un'istanza fittizia in setUp con i valori predefiniti per tutti i metodi sovrascritti e quindi in diversi test diversi modificare il valore restituito per alcuni dei metodi a seconda di cosa sto testando senza dover impostare il intero Mock. C'è un modo per fare questo?Posso modificare un metodo su PHPUnit Mock dopo averlo impostato?

Questo è quello che ho provato, ma l'approccio ingenuo non funziona. Il metodo restituisce ancora il valore dall'impostazione di attesa originale.

Prima impostazione:

$my_mock->expects($this->any()) 
     ->method('one_of_many_methods') 
     ->will($this->returnValue(true)); 

In un altro test prima di un'assert diversa:

$my_mock->expects($this->any()) 
     ->method('one_of_many_methods') 
     ->will($this->returnValue(false)); 

duplicati per questa domanda: PHPUnit Mock Change the expectations later, ma che uno non ha ottenuto risposte e ho pensato una nuova domanda potrebbe portare il problema in primo piano.

+0

AFAIK sfortunatamente non esiste tale possibilità con phpunit. Puoi usare ad esempio $ my_mock -> __ phpunit_hasMatchers(), ma non è esattamente quello che vuoi. Ovviamente è possibile impostare diversi valori di ritorno sullo stesso metodo con a) "at" matcher o b) "returnCallback" ma essi dipendono da un ordine di invocazione b) parametri di chiamata .. ma non è nemmeno quello che si sta cercando. Ti farò sapere capisco qualcosa di nuovo. – Cyprian

+0

Vedere anche http://stackoverflow.com/questions/5484602/mock-in-phpunit-multiple-configuration-of-the-same-method-with-different-argum/5484864#5484864 – bishop

risposta

3

Un modo per farlo sarebbe quello di utilizzare un data provider, qualcosa come:

/** 
* @dataProvider methodValueProvider 
*/ 
public function testMethodReturnValues($method, $expected) { 
    // ... 
    $my_mock->expects($this->any()) 
      ->method($method) 
      ->will($this->returnValue($expected)); 
    // ... 
} 

public function methodValueProvider() { 
    return array(
    array('one_of_many_methods', true), 
    array('another_one_of_many', false) 
); 
} 

Edit: Scusa, ho letto male la tua domanda. Quanto sopra è (ovviamente) non quello che stai cercando.

1

Non ho provato questo, ma non sono riuscito a impostare il Mock up nel setup, poi in ciascuna delle prove:

public function testMethodReturnsTrue 
{ 
    $this->my_mock->will($this->returnValue(true)); 
    $this->assertTrue(...); 
    ... 
} 

io non sono sicuro se questo funzionerà, come io sto cercando di imposta il metodo will() nel test, non quando è stata creata la simulazione iniziale.

5

Nei casi in cui si utilizza lo stesso metodo più di una volta, è necessario utilizzare la dichiarazione "at" con il conteggio corretto in cui viene eseguita nel codice. In questo modo PHPUnit sa quale intendi e può soddisfare correttamente l'aspettativa/asserzione.

seguito è riportato un esempio generico in cui il metodo 'run' viene utilizzato più volte:

public function testRunUsingAt() 
    { 
     $test = $this->getMock('Dummy'); 

     $test->expects($this->at(0)) 
      ->method('run') 
      ->with('f', 'o', 'o') 
      ->will($this->returnValue('first')); 

     $test->expects($this->at(1)) 
      ->method('run') 
      ->with('b', 'a', 'r') 
      ->will($this->returnValue('second')); 

     $test->expects($this->at(2)) 
      ->method('run') 
      ->with('l', 'o', 'l') 
      ->will($this->returnValue('third')); 

     $this->assertEquals($test->run('f', 'o', 'o'), 'first'); 
     $this->assertEquals($test->run('b', 'a', 'r'), 'second'); 
     $this->assertEquals($test->run('l', 'o', 'l'), 'third'); 
    } 

Penso che questo è quello che stai cercando, ma se sto equivoco per favore fatemelo sapere.

Ora in termini di derisione, puoi deriderlo tutte le volte che vuoi, ma non vorrai deriderlo con lo stesso nome del setup, altrimenti ogni volta che lo utilizzi ti stai riferendo al setup. Se hai bisogno di testare metodi simili in diversi scenari, allora prendi in giro per ogni test. È possibile creare una simulazione nella configurazione, ma per un test utilizzare un diverso simulato di un oggetto simile all'interno di un singolo test, ma non del nome globale.

+5

Penso che questo dovrebbe essere accettato rispondi, ma tieni presente che l'indice utilizzato per at() si basa sul numero totale di chiamate a tale oggetto fittizio, NON sul numero di chiamate a quel particolare metodo. Quindi se altre chiamate vengono fatte ad altri metodi sullo stesso oggetto, sarà necessario aumentare l'indice di conseguenza. – Russ

3

si potrebbe fare questo usando un callback lambda:

$one_of_many_methods_return = true; 
$my_mock->expects($this->any()) 
     ->method('one_of_many_methods') 
     ->will(
      $this->returnCallback(
       function() use (&$one_of_many_methods_return) { 
        return $one_of_many_methods_return; 
       } 
      )   
     ); 
$this->assertTrue($my_mock->one_of_many_methods()); 

$one_of_many_methods_return = false; 

$this->assertFalse($my_mock->one_of_many_methods());  

Nota la & nella dichiarazione use.

+0

Questo è utile per valori una tantum, sebbene sia più semplice per i metodi di test condividere proprietà ('$ this-> one_of_many_methods_return') rispetto ai riferimenti (' & $ one_of_many_methods_return'). Puoi anche usare direttamente i metodi, come '$ this-> returnCallback ([$ this, 'someMethod'])'. Si noti inoltre che PHP <5.5 potrebbe lamentarsi dell'uso di '$ this' e dei metodi/proprietà private/protette in funzioni anonime. – Warbo

1

Piuttosto che cercare di ignorare i metodi derisi, trovo più semplice scavalcare gli oggetti derisi stessi.Per esempio:

class ThingTest extends \PHPUnit_Framework_TestCase 
    public function setUp() 
    { 
     $this->initFoo(); 
     $this->initBar(); 
    } 

    public function testOne() 
    { 
     // Uses default [method => value] map for foo and bar 
     $this->assertSomething($this->thing->someMethod()); 
    } 

    public function testTwo() 
    { 
     // Override foo's map 
     $this->initFoo(['method1' => 'some other value']); 
     $this->assertSomethingElse($this->thing->someMethod()); 
    } 

    public function testThree() 
    { 
     // Override bar explicitly, so we can use 'once' 
     $this->initBar([]); 
     $this->bar->expects($this->once()) 
        ->method('method1'); 
     $this->thing->someOtherMethod(); 
    } 

    private function initFoo($methods = null) 
    { 
     $this->init('foo', 
        $this->getMock('Foo'), 
        is_null($methods)? ['method1' => 'default value 1'] 
            : $methods); 
    } 

    private function initBar($methods = null) 
    { 
     $this->init('bar', 
        $this->getMock('Bar'), 
        is_null($methods)? ['method1' => 'default value 1'] 
            : $methods); 
    } 

    private function init($name, $object, $methods) 
    { 
     $this->$name = $object; 
     foreach ($methods as $method => $value) { 
      $this->$name->expects($this->any()) 
         ->method($method) 
         ->will($this->returnValue($value)); 
     } 
     $this->thing = new Thing($this->foo, $this->bar); 
    } 
} 
0

È anche possibile eseguire i test in un processo separato:

/** 
* @runTestsInSeparateProcesses b/c we change the return value of same expectation 
* @see http://stackoverflow.com/questions/13631855 
*/ 
class ThingTest extends \PHPUnit_Framework_TestCase 
{ 
    public function setUp() { 
     $this->config = Mockery::mock('alias:Config'); 
    } 

    public function test_thing_with_valid_config() { 
     $this->config_set('default', 'valid'); 
     $sut = new \Thing(); 
    } 

    /** 
    * @dataProvider provides_broken_configs 
    * @expectedException \RuntimeException 
    */ 
    public function test_thing_with_broken_config($default) { 
     $this->config_set('default', $default); 
     $sut = new \Thing(); 
    } 

    public function provides_broken_configs() { 
     return [ [ null ] ]; 
    } 

    protected function config_set($key, $value) { 
     $this->config->shouldReceive('get')->with($key)->andReturn($value); 
    } 
} 

In questo esempio, mi capita di essere utilizzando derisione, ma il modello è lo stesso. Dal momento che ogni test ha una nuova memoria, non ci imbattiamo nella limitazione di "override" delle aspettative precedentemente impostate.