Вот мой класс:
public function __construct(Manager $moduleManager, Source\Yesno $yesNo)
{
$this->moduleManager = $moduleManager;
$this->yesNo = $yesNo;
}
public function my1()
{
$this->moduleManager->isOutputEnabled('');
$this->yesNo->toOptionArray();
}
public function my2()
{
$this->moduleManager->isOutputEnabled('');
$this->yesNo->toOptionArray();
}
Вот мой тест:
...
$this->observerMock = $this->getMock(
'path\to\Observer',
null,
[$this->moduleManagerMock, $this->yesNoMock],
'',
true
);
...
public function testMy1()
{
$this->moduleManagerMock->expects($this->exactly(2))->method('isOutputEnabled');
$this->yesNoMock->expects($this->exactly(2))->method('toOptionsArray');
$this->observerMock->my1();
$this->observerMock->my2();
}
Тест возвращается:
Ожидание не удалось для имени метода равно
когда вызывается 2 раза. Метод должен был быть вызван 2 раза,
на самом деле звонил 0 раз.
У меня вопрос: я сталкивался с такими вещами несколько раз, но каждый раз не могу понять, что происходит. Почему первое ожидание правильное, а второе не правильное?
Я забыл сказать, что сталкивался с такой ситуацией несколько раз.
Вот что я заметил. Используя xDebug, я увидел, что внутри теста
[this]
[moduleManager] => ModuleManager_Mock_Name_<hash #1>
[yesNo] => YesNo_Mock_Name_<hash #2>
[observerObject] =>
[moduleManager] => ModuleManager_Mock_Name_<hash #1>
[yesNo] => YesNo_Mock_Name_<hash #3> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
Объекты moduleManager имеют одинаковый кэш внутри объекта unittest и внутри объекта-наблюдателя. Если я применяю что-то на moduleMatcher — оно появляется в обоих местах
Хеш $ unittest-> yesNo отличается от хеша $ наблюдателя объекта. Если я устанавливаю matcher для какого-либо метода — он применяется только внутри класса unittest !!!
Почему так случилось? Как предотвратить создание разных объектов
Когда вы создаете объект через менеджер объектов
$this->observerMock = $objectManager->getObject(
'Observer',
[
'moduleManager' => $this->moduleManagerMock,
'yesNo' => $this->yesNoMock,
]
);
Переменные ‘moduleManager’ и ‘yesNo’ должны совпадать с переменными в конструкторе:
public function __construct(Manager $moduleManager, Source\Yesno $yesNo)
{
$this->moduleManager = $moduleManager;
$this->yesNo = $yesNo;
}
Вот код, где phpunit проверяет это:
foreach ($method->getParameters() as $parameter) {
$parameterName = $parameter->getName();
$argClassName = null;
$defaultValue = null;
if (array_key_exists($parameterName, $arguments)) {
$constructArguments[$parameterName] = $arguments[$parameterName];
continue;
}
Как правило, вы не стоит издеваться тестируемая система сам. Поскольку в вашем тестовом случае $this->observerMock
Объект сам по себе является объектом-заглушкой (который имитирует интерфейс другого класса, но без какой-либо реализации).
Это означает, что методы m1
а также m2
являются также фиктивные методы это ничего не сделает, когда их призовут. Впоследствии проверяемые методы на ваших зависимостях (moduleManagerMock
а также yesNoMock
) будут никогда не называться и именно поэтому ваши ожидания не оправдываются.
Чтобы правильно проверить желаемое поведение, используйте Observer
класс непосредственно:
public function setUp() {
$this->moduleManagerMock = $this->getMock(/*...*/);
$this->yesNoMock = $this->getMock(/*...*/);
// Do not generate a mock object of "Observer", but use the class
// under test itself!
$this->observer = new Observer(
$this->moduleManagerMock,
$this->yesNoMock
);
}
public function testM1() {
$this->moduleManagerMock->expects($this->exactly(2))
->method('isOutputEnabled');
$this->yesNoMock->expects($this->exactly(2))
->method('toOptionsArray');
$this->observer->my1();
$this->observer->my2();
}
Других решений пока нет …