Как я могу заставить PHPUnit MockObjects возвращать разные значения на основе параметра?


У меня есть макет объекта 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 имитировать объект, чтобы возвращать другое значение на основе его аргументов? И если да, то как?

Author: thanksd, 2008-11-10

11 answers

Используйте обратный вызов. например (прямо из документации 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, если это необходимо.

 107
Author: Howard Sandford, 2008-11-15 11:13:17

Из последних документов PHPUnit: "Иногда метод с заглушкой должен возвращать разные значения в зависимости от предопределенного списка аргументов. Вы можете использовать returnvaluemap() для создания карты, которая связывает аргументы с соответствующими возвращаемыми значениями".

$mock->expects($this->any())
    ->method('getConfigValue')
    ->will(
        $this->returnValueMap(
            array(
                array('firstparam', 'secondparam', 'retval'),
                array('modes', 'foo', array('Array', 'of', 'modes'))
            )
        )
    );
 91
Author: Nikola Ivancevic, 2016-03-10 13:50:53

У меня была похожая проблема (хотя и немного другая... Мне не нужно было другое возвращаемое значение, основанное на аргументах, но мне нужно было проверить, чтобы убедиться, что 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() был известен, но на практике это, вероятно, не слишком плохо.

 42
Author: Adam, 2015-04-29 20:48:05

Вы, вероятно, захотите выполнить обратный вызов в режиме ООП:

<?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];
    }
}
?>
 26
Author: Francis Lewis, 2011-01-12 00:01:45

Это не совсем то, о чем вы просите, но в некоторых случаях это может помочь:

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

Onconsecutivecalls - возвращает список значений в указанном порядке

 11
Author: Prokhor Sednev, 2014-09-09 13:12:54

Передайте двухуровневый массив, где каждый элемент представляет собой массив:

  • первыми являются параметры метода, а наименьшее - возвращаемое значение.

Пример:

->willReturnMap([
    ['firstArg', 'secondArg', 'returnValue']
])
 2
Author: antonmarin, 2017-04-04 14:55:12

Вы также можете вернуть аргумент следующим образом:

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

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

Как вы можете видеть в Издевательской документации , метод returnValue($index) позволяет возвращать данный аргумент.

 2
Author: Gabriel Gcia Fdez, 2018-02-02 13:41:15

Вы имеете в виду что-то подобное?

public function TestSomeCondition($condition){
  $mockObj = $this->getMockObject();
  $mockObj->setReturnValue('yourMethod',$condition);
}
 0
Author: eddy147, 2008-11-10 14:25:19

У меня была аналогичная проблема, которую я тоже не смог решить (о PHPUnit на удивление мало информации). В моем случае я просто сделал каждый тест отдельным тестом - известный вход и известный выход. Я понял, что мне не нужно было создавать макет объекта "мастер на все руки", мне нужен был только конкретный объект для конкретного теста, и поэтому я разделил тесты и могу тестировать отдельные аспекты моего кода как отдельный блок. Я не уверен, применимо ли это к вам или нет, но это вплоть до того, что вам нужно проверить.

 0
Author: JamShady, 2008-11-10 15:13:04
$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'),


    }
 -1
Author: jjoselon, 2017-05-09 18:33:01

Попробуйте:

->with($this->equalTo('one'),$this->equalTo('two))->will($this->returnValue('return value'));
 -2
Author: erenon, 2010-03-24 17:19:44