Як я можу змусити 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 = $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();
    // ...
}
?>

Виконайте будь-яку обробку, яку ви хочете, у зворотному дзвінку () і поверніть результат на основі ваших $ аргументів, якщо це необхідно.


2
Чи можете ви надати посилання на документацію? Я не можу знайти його з "Google"
Кріс Еріксон

6
Зауважте, що ви можете використовувати метод як зворотний виклик, передаючи масив, наприклад $this->returnCallback(array('MyClassTest','myCallback')).
Патрік Фішер

1
Слід також мати можливість прямо закрити його
Окрамій,

7
Це слід застосовувати лише в рідкісних випадках. Я б запропонував натомість використовувати returnValueMap, оскільки він не вимагає введення в зворотній зв'язок спеціальної логіки.
Герман Дж. Радтке III

1
Я не можу вам подякувати достатньо. Також при версіях PHP> 5.4 ви можете використовувати анонімну функцію як зворотний дзвінок. $this->returnCallback(function() { // ... })
відзначитись

110

З останніх документів phpUnit: "Іноді заглубленний метод повинен повертати різні значення залежно від попередньо визначеного списку аргументів. Ви можете використовувати 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

У мене була схожа проблема (хоча трохи інша ... мені не потрібно було різного повернутого значення на основі аргументів, але довелося перевірити, щоб переконатися, що два набори аргументів передаються одній і тій же функції). Я натрапив на використання подібного:

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

Це не ідеально, оскільки вимагає, щоб порядок двох дзвінків до 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']
])


0

Ви маєте на увазі щось подібне?

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

Я думаю, що це SimpleTest код, а не PHPUnit. Але ні, це не те, чого я хочу досягти. Скажіть, у мене був макетний об’єкт, який повернув слово на задане число. Моєму методу глузування потрібно повернути "один", коли його дзвонять з 1, "два", коли дзвонять з 2 і т. Д. $
Бен Даулінг

0

У мене була подібна проблема, яку я також не міг розробити (є надзвичайно мало інформації про PHPUnit). У моєму випадку я просто робив кожен тест окремим тестом - відомий вхід і відомий вихід. Я зрозумів, що мені не потрібно створювати макетний об'єкт jack-of-all-trades, мені потрібен був лише конкретний для конкретного тесту, і таким чином я розділив тести і можу перевірити окремі аспекти мого коду як окремий одиниця. Я не впевнений, може це стосується вас чи ні, але це все, що потрібно перевірити.


На жаль, це не спрацювало б у моїй ситуації. Макет передається методу, який я тестую, і метод тестування викликає знущений метод з різними аргументами. Цікаво знати, що ви не могли вирішити проблему. Здається, це може бути обмеженням PHPUnit.
Бен Даулінг

-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 з () приймає декілька аргументів, один матч для кожного параметра.
ТаЗ
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.