PHPUnit: в тесте игнорируются порядки ожиданий, когда один и тот же метод-заглушка вызывается несколько раз с разными аргументами

У меня есть метод (назовем его method2), который вызывает другой метод (назовем его method1) несколько раз, но с разными аргументами.

Вот класс, MyClass.php:

<?php

class MyClass
{

    public function method1($arg)
    {
        return 'arg was ' . $arg;
    }

    public function method2()
    {
        // Calling the first time with 'one'
        $this->method1('one');

        // Calling other functions
        // ...

        // Calling the first time with 'two'
        $this->method1('two');
    }
}

При тестировании я создаю заглушку для method1, чтобы контролировать, как / когда он вызывался и что он возвращал. В моем тесте для method2 я следую порядку выполнения кода внутри method2.

Вот тестовый класс, MyClassTest.php:

<?php

require_once 'MyClass.php';

class MyClassTest extends PHPUnit_Framework_TestCase
{

    /** @test */
    public function method2_was_called_successfully_with_one_and_then_two()
    {
        $myClassMock = $this->getMockBuilder('MyClass')
                            ->setMethods(['method1'])
                            ->getMock();

        $myClassMock->expects($this->once())
                    ->method('method1')
                    ->with($this->stringContains('one', true))
                    ->will($this->returnValue('arg was one'));

        // Testing code for the code between the two calls
        // ...

        $myClassMock->expects($this->once())
                    ->method('method1')
                    ->with($this->stringContains('two', true))
                    ->will($this->returnValue('arg was two'));

        $myClassMock->method2();
    }
}

В моем тесте похоже, что PHPUnit не следует этому порядку и застревает с последним (в нашем случае вторым) вызовом метода method1:

Был 1 сбой:

1) MyClassTest:: method2_was_called_successfully_with_one_and_then_two Ожидание не выполнено для имени метода равно, когда вызывается 1 раз (а) Параметр 0 для вызова MyClass::method1('one') не соответствует ожидаемому значению. Не удалось утверждать, что "один" содержит "два".

/path/to/the/files/MyClass.php:14 /path/to/the/files/MyClassTest.php:28

ОТКАЗЫ! Тесты: 1, Утверждения: 0, Сбои: 1.

Любая идея о том, что является основным, что я здесь скучаю / делаю неправильно?

1 ответ

Решение

Вы должны использовать at() вместо once() при настройке макета:

    $myClassMock = $this->getMockBuilder('MyClass')
                        ->setMethods(['method1'])
                        ->getMock();

    $myClassMock->expects($this->at(0))
                ->method('method1')
                ->with($this->stringContains('one', true))
                ->will($this->returnValue('arg was one'));

    $myClassMock->expects($this->at(1))
                ->method('method1')
                ->with($this->stringContains('two', true))
                ->will($this->returnValue('arg was two'));


    // Testing code
    // ....
    // ....

С другой стороны, мне кажется странным настраивать макет после того, как тестовый код уже выполнен. Обычный шаблон - это настроить все вызовы, которые должны получать фиктивные в начале теста. Затем выполните тест SUT и убедитесь, что все вызовы были сделаны (обычно этот последний шаг выполняется автоматически).

Другие вопросы по тегам