Làm cách nào tôi có thể nhận được PHPUnit MockObjects để trả về các giá trị khác nhau dựa trên một tham số?


141

Tôi đã có một đối tượng giả PHPUnit trả về 'return value'bất kể đối số của nó là gì:

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

Những gì tôi muốn có thể làm là trả về một giá trị khác dựa trên các đối số được truyền cho phương thức giả. Tôi đã thử một cái gì đó như:

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

Nhưng điều này khiến PHPUnit phàn nàn nếu giả lập không được gọi với đối số 'two', vì vậy tôi cho rằng định nghĩa methodToMock('two')ghi đè định nghĩa đầu tiên.

Vì vậy, câu hỏi của tôi là: Có cách nào để có được một đối tượng giả PHPUnit để trả về một giá trị khác dựa trên các đối số của nó không? Và nếu vậy thì thế nào?

Câu trả lời:


125

Sử dụng một cuộc gọi lại. ví dụ (trực tiếp từ tài liệu 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();
    // ...
}
?>

Thực hiện bất kỳ xử lý nào bạn muốn trong hàm gọi lại () và trả về kết quả dựa trên $ args của bạn nếu phù hợp.


2
Bạn có thể cung cấp một liên kết đến các tài liệu? Tôi dường như không thể tìm thấy nó với "Google"
Kris Erickson

6
Lưu ý rằng bạn có thể sử dụng một phương thức như một cuộc gọi lại bằng cách chuyển một mảng, ví dụ $this->returnCallback(array('MyClassTest','myCallback')).
Patrick Fisher

1
Cũng có thể chuyển trực tiếp một đóng cửa cho nó
Ocramius

7
Điều này chỉ nên được sử dụng trong những trường hợp hiếm hoi. Tôi sẽ đề nghị sử dụng returnValueMap thay vì nó không yêu cầu logic tùy chỉnh được viết trong cuộc gọi lại.
Herman J. Radtke III

1
Tôi không thể cảm ơn đủ. Ngoài ra, với các phiên bản PHP> 5.4, bạn có thể sử dụng một hàm ẩn danh làm cuộc gọi lại. $this->returnCallback(function() { // ... })
bmorenate

110

Từ các tài liệu phpUnit mới nhất: "Đôi khi, một phương thức gốc sẽ trả về các giá trị khác nhau tùy thuộc vào danh sách các đối số được xác định trước. Bạn có thể sử dụng returnValueMap () để tạo bản đồ liên kết các đối số với các giá trị trả về tương ứng."

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

3
Liên kết trong bài đã cũ, chính xác là ở đây: returnValueMap ()
hejdav

48

Tôi có một vấn đề tương tự (mặc dù hơi khác nhau ... Tôi không cần giá trị trả về khác nhau dựa trên các đối số, nhưng phải kiểm tra để đảm bảo 2 bộ đối số được truyền cho cùng một hàm). Tôi tình cờ sử dụng một cái gì đó như thế này:

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

Nó không hoàn hảo, vì nó yêu cầu thứ tự của 2 cuộc gọi đến foo (), nhưng trong thực tế, điều này có lẽ không quá tệ.


28

Bạn có thể muốn thực hiện một cuộc gọi lại theo kiểu 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

Nó không chính xác như những gì bạn yêu cầu, nhưng trong một số trường hợp, nó có thể giúp:

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

onConsceedCalls - trả về danh sách các giá trị theo thứ tự được chỉ định


4

Vượt qua hai cấp độ, trong đó mỗi phần tử là một mảng gồm:

  • đầu tiên là các tham số phương thức và ít nhất là giá trị trả về.

thí dụ:

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

2

Bạn cũng có thể trả về đối số như sau:

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

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

Như bạn có thể thấy trong tài liệu Mocking , phương thức returnValue($index)cho phép trả về đối số đã cho.


0

Bạn có ý nghĩa gì đó như thế này?

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

Tôi nghĩ đó là mã SimpleTest, không phải PHPUnit. Nhưng không, đó không phải là điều tôi muốn đạt được. Nói rằng tôi có một đối tượng giả đã trả lại một từ cho một số đã cho. Phương thức giả của tôi sẽ cần trả về "một" khi được gọi bằng 1, "hai" khi được gọi bằng 2, v.v.
Ben Dowling

0

Tôi đã có một vấn đề tương tự mà tôi không thể giải quyết được (có rất ít thông tin đáng ngạc nhiên về PHPUnit). Trong trường hợp của tôi, tôi chỉ thực hiện mỗi thử nghiệm riêng biệt - đầu vào đã biết và đầu ra đã biết. Tôi nhận ra rằng tôi không cần phải tạo một đối tượng giả toàn bộ giao dịch, tôi chỉ cần một đối tượng cụ thể cho một thử nghiệm cụ thể, và do đó tôi tách các thử nghiệm ra và có thể kiểm tra các khía cạnh riêng lẻ của mã của tôi như một riêng biệt đơn vị. Tôi không chắc liệu điều này có thể áp dụng cho bạn hay không, nhưng đó là những gì bạn cần kiểm tra.


Thật không may, điều đó sẽ không làm việc trong tình huống của tôi. Giả lập đang được truyền vào một phương thức tôi đang thử nghiệm và phương thức thử nghiệm gọi phương thức giả định đó với các đối số khác nhau. Thật thú vị khi biết rằng bạn không thể giải quyết vấn đề mặc dù. Có vẻ như điều này có thể là một giới hạn 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

Thử :

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

Câu trả lời này không áp dụng cho câu hỏi ban đầu, nhưng nó nêu chi tiết một vấn đề tương tự tôi gặp phải: xác minh rằng một bộ thông số nhất định được cung cấp. PHPUnit's with () chấp nhận nhiều đối số, một đối sánh cho mỗi tham số.
TaZ
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.