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?

Ben Dowling
sumber

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.

Howard Sandford
sumber
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'))
            )
        )
    );
Nikola Ivancevic
sumber
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.

Adam
sumber
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];
    }
}
?>
Francis Lewis
sumber
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

Prokhor Sednev
sumber
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']
])
antonmarin
sumber
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.

Gabriel Gcia Fdez
sumber
0

Maksud Anda sesuatu seperti ini?

public function TestSomeCondition($condition){
  $mockObj = $this->getMockObject();
  $mockObj->setReturnValue('yourMethod',$condition);
}
eddy147
sumber
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.

JamShady
sumber
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'),


    }
jjoselon
sumber
-2

Coba:

->with($this->equalTo('one'),$this->equalTo('two))->will($this->returnValue('return value'));
erenon
sumber
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