phpunit: насмешливый абстрактный класс для хинтинга типов? - PullRequest
0 голосов
/ 26 апреля 2018

Я пытаюсь протестировать вспомогательную функцию, которая использует некоторый экземпляр Form, абстрактный класс с одним абстрактным методом, process (который на самом деле не вызывается из протестированной функции).

function get_field_class(Form $form, $field) {
  return $form->get_errors_for($field) ? 'error' : '';
}

У меня такой вопрос, как мне высмеять абстрактный класс Form, чтобы он принимался аннотацией типа get_field_class?

Я пытался передатьНепосредственно макетный экземпляр:

$form = $this->getMockForAbstractClass(Form::class)
  ->method('process')
  ->will($this->returnValue(true));

$this->assertEquals('error', $this->wrapper->get_field_class($form, 'foo'));

Это выдает ошибку типа: Аргумент 1 ... должен быть экземпляром формы, экземпляром PHPUnit \ Framework \ MockObject \ Builder \ InvocationMocker, заданным

Я попытался установить имя ложного класса явно:

$form = $this->getMockBuilder(Form::class)
  ->setMockClassName(Form::class)
  ->getMockForAbstractClass(Form::class)
  // ...

Это выдает предупреждение Класс "Форма" уже существует .(Предупреждения приводятся к исключениям или ошибкам в моей конфигурации, поэтому это не приемлемые решения.)

Я пытался отключить автозагрузку (возможно, phpunit по какой-то причине с нетерпением загружает класс Form ??):

$form = $this->getMockBuilder(Form::class)
  ->disableAutoload()
  ->setMockClassName(Form::class)
  ->getMockForAbstractClass(Form::class)
  // ...

Это просто выдает предупреждение: Класс "Форма" не существует.

1 Ответ

0 голосов
/ 26 апреля 2018

В вашем первом примере, который не сработал, вы фактически не передаете фиктивный объект.Для того чтобы это работало, код должен быть:

$form = $this->getMockForAbstractClass(Form::class);

$form->expects($this->once())
  ->method('process')
  ->will($this->returnValue(true));

$this->assertEquals('error', $this->wrapper->get_field_class($form, 'foo'));

will() возвращает объект, который PHPUnit использует для проверки правильности вызова фиктивных объектов.

Как упоминалось вкомментарии, удаляющие setMockClassName из вызова MockBuilder, будут работать.

$form = $this->getMockBuilder(Form::class)
  ->getMockForAbstractClass(Form::class);

Опять же, с помощью этой формы вам нужно будет установить ожидания для вызовов на process отдельно.

Ошибкавы получаете, потому что класс уже существует, и PHP не позволит вам создать два класса с одинаковым именем.В любом случае вам не нужно устанавливать имя для этого, так как PHPUnit создаст новый объект, расширяющий ваш Form класс.

Посмотрите на пример из документации PHPUnit: https://phpunit.readthedocs.io/en/7.1/test-doubles.html#mocking-traits-and-abstract-classes

<?php
use PHPUnit\Framework\TestCase;

abstract class AbstractClass
{
    public function concreteMethod()
    {
        return $this->abstractMethod();
    }

    public abstract function abstractMethod();
}

class AbstractClassTest extends TestCase
{
    public function testConcreteMethod()
    {
        $stub = $this->getMockForAbstractClass(AbstractClass::class);

        $stub->expects($this->any())
             ->method('abstractMethod')
             ->will($this->returnValue(true));

        $this->assertTrue($stub->concreteMethod());
    }
}
?>
...