Come posso ottenere PHPUnit MockObjects per restituire valori diversi in base a un parametro?


141

Ho un oggetto finto PHPUnit che restituisce 'return value'indipendentemente dai suoi argomenti:

// From inside a test...
$mock = $this->getMock('myObject', 'methodToMock');
$mock->expects($this->any))
     ->method('methodToMock')
     ->will($this->returnValue('return value'));

Quello che voglio poter fare è restituire un valore diverso in base agli argomenti passati al metodo simulato. Ho provato qualcosa del tipo:

$mock = $this->getMock('myObject', 'methodToMock');

// methodToMock('one')
$mock->expects($this->any))
     ->method('methodToMock')
     ->with($this->equalTo('one'))
     ->will($this->returnValue('method called with argument "one"'));

// methodToMock('two')
$mock->expects($this->any))
     ->method('methodToMock')
     ->with($this->equalTo('two'))
     ->will($this->returnValue('method called with argument "two"'));

Ma questo fa sì che PHPUnit si lamenti se il mock non viene chiamato con l'argomento 'two', quindi presumo che la definizione di methodToMock('two')sovrascriva la definizione del primo.

Quindi la mia domanda è: c'è un modo per ottenere un oggetto simulato PHPUnit per restituire un valore diverso in base ai suoi argomenti? E se sì, come?

Risposte:


125

Usa un callback. ad es. (direttamente dalla documentazione di PHPUnit):

<?php
class StubTest extends PHPUnit_Framework_TestCase
{
    public function testReturnCallbackStub()
    {
        $stub = $this->getMock(
          'SomeClass', array('doSomething')
        );

        $stub->expects($this->any())
             ->method('doSomething')
             ->will($this->returnCallback('callback'));

        // $stub->doSomething() returns callback(...)
    }
}

function callback() {
    $args = func_get_args();
    // ...
}
?>

Esegui qualsiasi elaborazione tu voglia nel callback () e restituisci il risultato in base ai tuoi $ args come appropriato.


2
Potete fornire un link alla documentazione? Non riesco a trovarlo con "Google"
Kris Erickson il

6
Si noti che è possibile utilizzare un metodo come callback passando un array, ad es $this->returnCallback(array('MyClassTest','myCallback')).
Patrick Fisher,

1
Dovrebbe anche essere possibile passare direttamente una chiusura ad esso
Ocramius

7
Questo dovrebbe essere usato solo in rari casi. Suggerirei invece di utilizzare returnValueMap in quanto non richiede la logica personalizzata da scrivere nel callback.
Herman J. Radtke III,

1
Non posso ringraziarti abbastanza. Inoltre, con le versioni di PHP> 5.4, è possibile utilizzare una funzione anonima come callback. $this->returnCallback(function() { // ... })
bmorenate il

110

Dagli ultimi documenti phpUnit: "A volte un metodo stubbed dovrebbe restituire valori diversi a seconda di un elenco predefinito di argomenti. È possibile utilizzare returnValueMap () per creare una mappa che associa gli argomenti ai corrispondenti valori restituiti."

$mock->expects($this->any())
    ->method('getConfigValue')
    ->will(
        $this->returnValueMap(
            array(
                array('firstparam', 'secondparam', 'retval'),
                array('modes', 'foo', array('Array', 'of', 'modes'))
            )
        )
    );

3
Il link in post è vecchio, quello corretto è qui: returnValueMap ()
hejdav

49

Ho avuto un problema simile (anche se leggermente diverso ... Non avevo bisogno di un valore di ritorno diverso basato su argomenti, ma ho dovuto testare per assicurarsi che 2 set di argomenti fossero passati alla stessa funzione). Mi sono imbattuto nell'usare qualcosa del genere:

$mock = $this->getMock();
$mock->expects($this->at(0))
    ->method('foo')
    ->with(...)
    ->will($this->returnValue(...));

$mock->expects($this->at(1))
    ->method('foo')
    ->with(...)
    ->will($this->returnValue(...));

Non è perfetto, poiché richiede che l'ordine delle 2 chiamate a foo () sia noto, ma in pratica questo probabilmente non è poi così male.


28

Probabilmente vorrai fare una richiamata in modo OOP:

<?php
class StubTest extends PHPUnit_Framework_TestCase
{
    public function testReturnAction()
    {
        $object = $this->getMock('class_name', array('method_to_mock'));
        $object->expects($this->any())
            ->method('method_to_mock')
            ->will($this->returnCallback(array($this, 'returnCallback'));

        $object->returnAction('param1');
        // assert what param1 should return here

        $object->returnAction('param2');
        // assert what param2 should return here
    }

    public function returnCallback()
    {
        $args = func_get_args();

        // process $args[0] here and return the data you want to mock
        return 'The parameter was ' . $args[0];
    }
}
?>

16

Non è esattamente quello che chiedi, ma in alcuni casi può aiutare:

$mock->expects( $this->any() ) )
 ->method( 'methodToMock' )
 ->will( $this->onConsecutiveCalls( 'one', 'two' ) );

onConsecutiveCalls : restituisce un elenco di valori nell'ordine specificato


4

Passa un array a due livelli, dove ogni elemento è un array di:

  • in primo luogo sono i parametri del metodo e il meno è il valore restituito.

esempio:

->willReturnMap([
    ['firstArg', 'secondArg', 'returnValue']
])

2

È inoltre possibile restituire l'argomento come segue:

$stub = $this->getMock(
  'SomeClass', array('doSomething')
);

$stub->expects($this->any())
     ->method('doSomething')
     ->will($this->returnArgument(0));

Come puoi vedere nella documentazione di derisione , il metodo returnValue($index)consente di restituire l'argomento dato.


0

Intendi qualcosa come questo?

public function TestSomeCondition($condition){
  $mockObj = $this->getMockObject();
  $mockObj->setReturnValue('yourMethod',$condition);
}

Penso che sia il codice SimpleTest, non PHPUnit. Ma no, non è quello che voglio ottenere. Supponiamo di avere un oggetto finto che ha restituito una parola per un determinato numero. Il mio metodo simulato dovrebbe restituire "uno" quando viene chiamato con 1, "due" quando viene chiamato con 2 ecc. $
Ben Dowling,

0

Ho avuto un problema simile che non riuscivo a risolvere (ci sono sorprendentemente poche informazioni su PHPUnit). Nel mio caso, ho appena fatto ogni test separato test - input noto e output noto. Mi sono reso conto che non avevo bisogno di creare un oggetto fittizio tuttofare, ne avevo solo bisogno per uno specifico per un test specifico, quindi ho separato i test e posso testare i singoli aspetti del mio codice come separato unità. Non sono sicuro che questo possa essere applicabile a te o no, ma dipende da ciò che devi testare.


Purtroppo ciò non funzionerebbe nella mia situazione. La simulazione viene passata a un metodo che sto testando e il metodo di prova chiama il metodo deriso con diversi argomenti. È interessante sapere che non è possibile risolvere il problema. Sembra che questo potrebbe essere un limite di PHPUnit.
Ben Dowling,

-1
$this->BusinessMock = $this->createMock('AppBundle\Entity\Business');

    public function testBusiness()
    {
        /*
            onConcecutiveCalls : Whether you want that the Stub returns differents values when it will be called .
        */
        $this->BusinessMock ->method('getEmployees')
                                ->will($this->onConsecutiveCalls(
                                            $this->returnArgument(0),
                                            $this->returnValue('employee')                                      
                                            )
                                      );
        // first call

        $this->assertInstanceOf( //$this->returnArgument(0),
                'argument',
                $this->BusinessMock->getEmployees()
                );
       // second call


        $this->assertEquals('employee',$this->BusinessMock->getEmployees()) 
      //$this->returnValue('employee'),


    }

-2

Provare :

->with($this->equalTo('one'),$this->equalTo('two))->will($this->returnValue('return value'));

Questa risposta non si applica alla domanda originale, ma descrive in dettaglio un problema simile che ho riscontrato: verificare che venga fornito un determinato set di parametri. PHPUnit's with () accetta più argomenti, un matcher per ogni parametro.
TaZ,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.