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