ฉันจะรับ PHPUnit MockObjects เพื่อคืนค่าต่าง ๆ ตามพารามิเตอร์ได้อย่างไร


141

ฉันมีวัตถุจำลอง PHPUnit ที่ส่งคืน'return value'ไม่ว่าอาร์กิวเมนต์จะเป็นอย่างไร

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

สิ่งที่ฉันต้องการจะทำคือคืนค่าที่แตกต่างกันตามอาร์กิวเมนต์ที่ส่งไปยังเมธอด mock ฉันลองทำสิ่งที่ชอบแล้ว:

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

แต่สิ่งนี้ทำให้ PHPUnit บ่นหากไม่มีการจำลองด้วยอาร์กิวเมนต์'two'ดังนั้นฉันคิดว่าคำจำกัดความของการmethodToMock('two')เขียนทับคำจำกัดความของคำแรก

ดังนั้นคำถามของฉันคือ: มีวิธีการรับวัตถุ PHPUnit จำลองเพื่อส่งกลับค่าที่แตกต่างกันตามอาร์กิวเมนต์? และถ้าเป็นเช่นนั้นได้อย่างไร

คำตอบ:


125

ใช้โทรกลับ เช่น (ตรงจากเอกสาร 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();
    // ...
}
?>

ทำสิ่งที่คุณต้องการในการเรียกกลับ () และส่งกลับผลลัพธ์ตาม $ args ของคุณตามความเหมาะสม


2
คุณสามารถให้ลิงค์ไปยังเอกสารประกอบได้หรือไม่? ฉันดูเหมือนจะไม่พบมันด้วย "the Google"
Kris Erickson

6
$this->returnCallback(array('MyClassTest','myCallback'))โปรดทราบว่าคุณสามารถใช้วิธีการเช่นการติดต่อกลับโดยผ่านอาร์เรย์เช่น
แพทริคฟิชเชอร์

1
มันควรจะเป็นไปได้ที่จะผ่านการปิดโดยตรง
Ocramius

7
ควรใช้ในบางกรณีเท่านั้น ฉันขอแนะนำให้ใช้returnValueMapแทนเนื่องจากไม่จำเป็นต้องใช้ตรรกะที่กำหนดเองเพื่อเขียนในการติดต่อกลับ
Herman J. Radtke III

1
ฉันไม่สามารถขอบคุณได้มากพอ นอกจากนี้สำหรับรุ่น PHP> 5.4 คุณสามารถใช้ฟังก์ชั่นที่ไม่ระบุตัวตนเป็นหมายเลขโทรกลับ $this->returnCallback(function() { // ... })
bmorenate

110

จาก phpUnit docs ล่าสุด: "บางครั้งเมธอด stubbed ควรส่งคืนค่าที่แตกต่างกันขึ้นอยู่กับรายการอาร์กิวเมนต์ที่กำหนดไว้ล่วงหน้าคุณสามารถใช้returnValueMap ()เพื่อสร้างแผนที่ที่เชื่อมโยงอาร์กิวเมนต์กับค่าส่งคืนที่สอดคล้องกัน"

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

3
ลิงก์ในโพสต์เก่าแก้ไขได้ที่นี่: returnValueMap ()
hejdav

49

ฉันมีปัญหาที่คล้ายกัน (แม้ว่าจะแตกต่างกันเล็กน้อย ... ฉันไม่ต้องการค่าส่งคืนที่แตกต่างกันตามอาร์กิวเมนต์ แต่ต้องทดสอบเพื่อให้แน่ใจว่ามีการส่งอาร์กิวเมนต์ 2 ชุดไปยังฟังก์ชันเดียวกัน) ฉันสะดุดกับการใช้สิ่งนี้:

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

มันไม่สมบูรณ์แบบเพราะมันต้องการคำสั่งของ 2 การเรียกไปที่ foo () แต่ในทางปฏิบัติมันอาจจะไม่แย่เกินไป


28

คุณอาจต้องการโทรกลับในแบบ 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

มันไม่ใช่สิ่งที่คุณถาม แต่ในบางกรณีมันสามารถช่วย:

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

onConsecutiveCalls - ส่งคืนรายการค่าตามลำดับที่ระบุ


4

ผ่านอาร์เรย์สองระดับโดยที่แต่ละองค์ประกอบเป็นอาร์เรย์ดังนี้

  • อันดับแรกคือพารามิเตอร์เมธอดและอย่างน้อยคือค่าส่งคืน

ตัวอย่าง:

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

2

คุณยังสามารถส่งคืนอาร์กิวเมนต์ดังนี้

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

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

ดังที่คุณเห็นในเอกสารการเยาะเย้ยวิธีการreturnValue($index)อนุญาตให้ส่งคืนอาร์กิวเมนต์ที่กำหนด


0

คุณหมายถึงอะไรเช่นนี้?

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

ฉันคิดว่าเป็นรหัส SimpleTest ไม่ใช่ PHPUnit แต่ไม่มันไม่ใช่สิ่งที่ฉันต้องการบรรลุ ว่าฉันมีวัตถุจำลองที่คืนคำสำหรับตัวเลขที่กำหนด วิธีการเยาะเย้ยของฉันจะต้องกลับ "หนึ่ง" เมื่อเรียกด้วย 1, "สอง" เมื่อเรียกด้วย 2 ฯลฯ $
Ben Dowling

0

ฉันมีปัญหาที่คล้ายกันซึ่งฉันไม่สามารถทำงานได้เช่นกัน (มีข้อมูลเล็กน้อยเกี่ยวกับ PHPUnit อย่างน่าประหลาดใจ) ในกรณีของฉันฉันเพิ่งทำแบบทดสอบแต่ละแบบแยกกันแบบทดสอบ - อินพุตที่รู้จักและเอาต์พุตที่รู้จัก ฉันรู้ว่าฉันไม่จำเป็นต้องสร้างวัตถุจำลองแจ็ค - ออฟ - - - - - - - - ทุก - ธุรกิจฉันต้องการเพียงสิ่งที่เฉพาะเจาะจงสำหรับการทดสอบที่เฉพาะเจาะจงและดังนั้นฉันจึงแยกการทดสอบออกและสามารถทดสอบแต่ละด้านของรหัสของฉันแยกต่างหาก หน่วย ฉันไม่แน่ใจว่าสิ่งนี้อาจใช้ได้กับคุณหรือไม่ แต่นั่นเป็นสิ่งที่คุณต้องทำการทดสอบ


น่าเสียดายที่มันใช้ไม่ได้กับสถานการณ์ของฉัน การจำลองนั้นกำลังถูกส่งผ่านไปยังวิธีที่ฉันกำลังทดสอบและวิธีการทดสอบเรียกวิธีการที่ถูกเยาะเย้ยด้วยอาร์กิวเมนต์ที่แตกต่างกัน มันน่าสนใจที่จะรู้ว่าคุณไม่สามารถแก้ปัญหาได้ ดูเหมือนว่านี่อาจเป็นข้อ จำกัด ของ 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

ลอง :

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

คำตอบนี้ใช้ไม่ได้กับคำถามดั้งเดิม แต่มีรายละเอียดของปัญหาที่คล้ายกันที่ฉันมี: ตรวจสอบว่ามีการจัดเตรียมพารามิเตอร์ชุดหนึ่งให้ PHPUnit's with () ยอมรับอาร์กิวเมนต์หลายตัวหนึ่งตัวจับคู่สำหรับแต่ละพารามิเตอร์
TaZ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.