В phpunit, существует ли метод, аналогичный onconsecutivecalls для использования внутри метода "с"?

Используя PHPUnit, я издеваюсь над pdo, но я пытаюсь найти способ подготовить несколько заявлений о запросе базы данных.

$pdo = $this->getPdoMock();
$stmt = $this->getPdoStatementMock($pdo);

$pdo->expects($this->any())
    ->method('prepare')
    ->with($this->equalTo($title_query))
    ->will($this->returnValue($stmt));

$title_stmt = $pdo->prepare($title_query);
$desc_stmt = $pdo->prepare($desc_query);

Я хочу передать что-то похожее на onConsecutiveCalls для метода "with", поэтому я могу подготовить несколько операторов, как показано выше. Как бы вы это сделали?

Ответ 1

Вы можете сопоставлять последовательные вызовы одного и того же метода, записывая отдельные ожидания с помощью $this->at() вместо $this->any():

$pdo->expects($this->at(0))
    ->method('prepare')
    ->with($this->equalTo($title_query))
    ->will($this->returnValue($stmt));

$pdo->expects($this->at(1))
    ->method('prepare')
    ->with($this->equalTo($desc_query))
    ->will($this->returnValue($stmt));

$title_stmt = $pdo->prepare($title_query);
$desc_stmt = $pdo->prepare($desc_query);

Ответ 2

PHPUnit 4.1 получил новый метод withConsecutive(). Из Test Double Chapter:

class FooTest extends PHPUnit_Framework_TestCase
{
    public function testFunctionCalledTwoTimesWithSpecificArguments()
    {
        $mock = $this->getMock('stdClass', array('set'));
        $mock->expects($this->exactly(2))
             ->method('set')
             ->withConsecutive(
                 array($this->equalTo('foo'), $this->greaterThan(0)),
                 array($this->equalTo('bar'), $this->greaterThan(0))
             );

        $mock->set('foo', 21);
        $mock->set('bar', 48);
    }
}

Каждый аргумент withConsecutive() предназначен для одного вызова указанного метода.

Ответ 3

В более поздних версиях PHPUnit принятый ответ можно упростить.

Кроме того, хотя это не имеет непосредственного отношения к исходному вопросу, вы можете легко предоставить разные возвращаемые значения для каждого вызова метода с помощью метода willReturnOnConsecutiveCalls.

$pdo->expects($this->exactly(2))
    ->method('prepare')
    ->withConsecutive(
        $this->equalTo($title_query),
        $this->equalTo($desc_query)
    )
    ->willReturnOnConsecutiveCalls(
        $stmt, // returned on the 1st call to prepare()
        $stmt  // returned on the 2nd call to prepare()
    );

Ответ 4

Единственное, что я нашел, похожий на то, что вы просите, это использовать 'at':

$mock->expects($this->at(0))->method // etc
$mock->expects($this->at(1))->method // etc

Таким образом, вы устанавливаете ожидания в первый раз, когда он вызывается (в 0), второй раз и т.д.

Ответ 5

Несколько человек отметили, что в ($ index) можно использовать для конкретных экземпляров вызовов метода. Дэвид Х. и Вика пояснили, что $index подсчитывает ВСЕ вызовы ВСЕМ издеваемым методам объекта.

Кроме того, может быть стоит отметить, что в Test Doubles Chapter документации PHPunit есть предупреждение об этом. Он указывает, что использование at() должно выполняться с осторожностью, поскольку это может привести к хрупким испытаниям, которые слишком сильно зависят от конкретной реализации.

Ответ 6

Я использую dataProvider и нуждаюсь в переменном количестве выполненных запросов, которые я могу использовать для заполнения моего провайдера, я хочу предоставить массив запросов, которые должны быть выполнены. Проблема с withConsecutive заключается в том, что он принимает переменное количество массивов, каждый из которых представляет собой массив аргументов.

Я решил это, используя следующий код, это чувствует себя "взломанным", но работает:

$withConsecutiveArgs = [
    [$this->equalTo($title_query)], 
    [$this->equalTo($desc_query)],
    ..., 
    N queries
];
$withConsecutiveReturns = [
    $title_stmt, 
    $desc_stmt,
    ...,
    N returns
];

$methodMock = $pdo->expects($this->exactly(count($args))->method('prepare');
$methodMock->getMatcher()->parametersMatcher = new \PHPUnit_Framework_MockObject_Matcher_ConsecutiveParameters($withConsecutiveArgs);
$methodMock->will(new \PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls($withConsecutiveReturns));

$title_stmt = $pdo->prepare($title_query);
$desc_stmt = $pdo->prepare($desc_query);
...
$N_s_stmt = $pdo->prepare($N_s_query);

Я надеюсь, что он будет корректно развиваться с будущими версиями PhpUnit (что неуверенно, поскольку я полагаюсь на внутренности lib, но что все, что нужно сделать unit test, возможно, придется реорганизовать, но производство не пострадает от что)