Bagaimana saya bisa mendapatkan PHPUnit MockObjects untuk mengembalikan nilai yang berbeda berdasarkan parameter?


141

Saya punya objek tiruan PHPUnit yang mengembalikan 'return value'apa pun argumennya:

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

Apa yang ingin saya lakukan adalah mengembalikan nilai yang berbeda berdasarkan argumen yang diteruskan ke metode mock. Saya sudah mencoba sesuatu seperti:

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

Tapi ini menyebabkan PHPUnit mengeluh jika mock tidak dipanggil dengan argumen 'two', jadi saya berasumsi bahwa definisi methodToMock('two')menimpa definisi yang pertama.

Jadi pertanyaan saya adalah: Apakah ada cara untuk mendapatkan objek tiruan PHPUnit untuk mengembalikan nilai yang berbeda berdasarkan argumennya? Dan jika demikian, bagaimana caranya?

Jawaban:


125

Gunakan panggilan balik. misalnya (langsung dari dokumentasi 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();
    // ...
}
?>

Lakukan pemrosesan apa pun yang Anda inginkan dalam callback () dan kembalikan hasilnya berdasarkan $ args Anda jika perlu.


2
Bisakah Anda memberikan tautan ke dokumentasi? Saya tidak dapat menemukannya dengan "Google"
Kris Erickson

6
Perhatikan bahwa Anda dapat menggunakan metode sebagai callback dengan melewatkan array, mis $this->returnCallback(array('MyClassTest','myCallback')).
Patrick Fisher

1
Seharusnya juga dimungkinkan untuk melakukan penutupan secara langsung
Ocramius

7
Ini harus digunakan hanya dalam kasus yang jarang terjadi. Saya akan menyarankan menggunakan returnValueMap sebagai gantinya karena tidak memerlukan logika khusus untuk ditulis dalam callback.
Herman J. Radtke III

1
Saya tidak bisa cukup berterima kasih. Juga, dengan versi PHP> 5.4, Anda dapat menggunakan fungsi anonim sebagai panggilan balik. $this->returnCallback(function() { // ... })
bmorenate

110

Dari dokumen phpUnit terbaru: "Kadang-kadang metode stubbed harus mengembalikan nilai yang berbeda tergantung pada daftar argumen yang telah ditentukan. Anda dapat menggunakan returnValueMap () untuk membuat peta yang mengaitkan argumen dengan nilai balik yang sesuai."

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

3
Tautan dalam posting sudah tua, yang benar ada di sini: returnValueMap ()
hejdav

49

Saya memiliki masalah yang sama (walaupun sedikit berbeda ... Saya tidak perlu nilai pengembalian yang berbeda berdasarkan argumen, tetapi harus menguji untuk memastikan 2 set argumen diteruskan ke fungsi yang sama). Saya menemukan sesuatu yang seperti ini:

$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(...));

Ini tidak sempurna, karena mengharuskan urutan 2 panggilan ke foo () diketahui, tetapi dalam praktiknya ini mungkin tidak terlalu buruk.


28

Anda mungkin ingin melakukan panggilan balik dengan cara 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

Bukan apa yang Anda minta, tetapi dalam beberapa kasus dapat membantu:

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

onConsecutiveCalls - mengembalikan daftar nilai dalam urutan yang ditentukan


4

Lewati dua level array, di mana setiap elemen adalah array dari:

  • pertama adalah parameter metode, dan paling tidak adalah nilai balik.

contoh:

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

2

Anda juga dapat mengembalikan argumen sebagai berikut:

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

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

Seperti yang dapat Anda lihat di dokumentasi Mocking , metode ini returnValue($index)memungkinkan untuk mengembalikan argumen yang diberikan.


0

Maksud Anda sesuatu seperti ini?

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

Saya pikir itu kode SimpleTest, bukan PHPUnit. Tapi tidak, bukan itu yang ingin saya capai. Katakanlah saya punya objek tiruan yang mengembalikan kata untuk nomor yang diberikan. Metode tiruan saya perlu mengembalikan "satu" ketika dipanggil dengan 1, "dua" ketika dipanggil dengan 2 dll. $
Ben Dowling

0

Saya memiliki masalah serupa yang tidak bisa saya selesaikan juga (hanya ada sedikit informasi tentang PHPUnit). Dalam kasus saya, saya hanya membuat setiap tes terpisah dengan input yang dikenal dan output yang dikenal. Saya menyadari bahwa saya tidak perlu membuat objek tiruan jack-of-all-trade, saya hanya membutuhkan satu spesifik untuk tes tertentu, dan dengan demikian saya memisahkan tes dan dapat menguji aspek individu dari kode saya sebagai terpisah satuan. Saya tidak yakin apakah ini mungkin berlaku untuk Anda atau tidak, tetapi hanya itu yang perlu Anda uji.


Sayangnya itu tidak akan berfungsi dalam situasi saya. Mock sedang dilewatkan ke dalam metode yang saya uji, dan metode pengujian memanggil metode mocked dengan argumen yang berbeda. Sangat menarik untuk mengetahui bahwa Anda tidak dapat menyelesaikan masalah. Sepertinya ini bisa menjadi batasan 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

Coba:

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

Jawaban ini tidak berlaku untuk pertanyaan awal, tetapi merinci masalah serupa yang saya miliki: memverifikasi bahwa serangkaian parameter disediakan. PHPUnit dengan () menerima beberapa argumen, satu pencocokan untuk setiap parameter.
TaZ
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.