Использование PHPUnit для макетирования программно определенного метода, не указанного в тестируемом классе - PullRequest
5 голосов
/ 04 января 2012

Использование PHPUnit 3.6 Я пытаюсь протестировать метод exec() в следующем классе контроллера. Этот метод делает две вещи:

  1. Определяет имя метода для вызова на основе существующих свойств объекта и ...
  2. Если определенный метод контроллера может быть вызван, он выполняется, а если нет, метод генерирует исключение

(упрощенный) исходный код выглядит следующим образом:

abstract class CLIController extends Controller
{
  /* irrelevant class details here */

  public function exec()
  {
    $action = ! empty($this->opts->args[0])
      ? $this->opts->args[0]
      : $this->default_action;

    if ( ! $action || ! is_callable(array($this, $action))) {
      $msg = 'Invalid controller action specified';
      throw new LogicException($msg);
    } else {
      $this->$action(); // <---- trying to get code coverage on this line!
    }
  }
}

Так что моя проблема ...

Я не могу понять, как получить покрытие по этой части кода:

} else {
  $this->$action();
}

потому что я не уверен, как (или что это даже возможно) проверить вызов метода, имя которого неизвестно в контексте абстрактного класса. Опять же: вызываемый метод объявлен в дочерних классах. Обычно я просто высмеиваю абстрактный метод, но не могу в этом случае, потому что метод еще не существует - он будет определен детский класс.

Какой может быть ответ ...

  • ??? Вполне возможно, что эту строку даже не нужно покрывать, потому что она по существу полагается на способность PHP правильно вызывать вызываемый метод класса. Если я успешно проверю, что exec() выдает исключение, когда это должно произойти, я знаю, что правильное функционирование рассматриваемой строки зависит от правильного функционирования PHP. Означает ли это, что нужно проверить его в первую очередь ???
  • Если есть какой-то способ смоделировать абстрактный класс и создать метод с известным именем для добавления в проверяемый класс , это решило бы мою проблему, и я пытался это сделать безуспешно далеко.
  • Я знаю, что могу создать дочерний класс с известным именем метода, но я не верю, что это хорошая идея - создать конкретный дочерний класс просто для проверки абстрактного родителя.
  • Возможно, мне нужно провести рефакторинг. Одна вещь, которую я не хочу делать, это оставлять дочерние классы для реализации функции exec() самостоятельно.

Что я пробовал ...

  • Использовать некоторые возможности PHP-рефлексии безрезультатно - возможно, это связано с моей неопытностью в рефлексии, а не с его неспособностью справиться с этим делом.
  • Переходя от руководства к PHPUnit к документации по API. К сожалению, как бы ни был хорош PHPUnit, я часто нахожу документацию по API немного легкой.

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

1 Ответ

5 голосов
/ 04 января 2012

Я не согласен с вашим условием, что «не очень хорошая идея создавать конкретный дочерний класс просто для проверки абстрактного родителя».Я делаю это довольно часто при тестировании абстрактных классов и обычно называю конкретный подкласс после теста, чтобы прояснить его.

class CLIControllerTest extends PHPUnit_Framework_TestCase
{
    public function testCallsActionMethod()
    {
        $controller = new CLIControllerTest_WithActionMethod(...);
        // set $controller->opts->args[0] to 'action'
        $controller->exec();
        self::assertTrue($controller->called, 'Action method was called');
    }
}

class CLIControllerTest_WithActionMethod extends CLIController
{
    public $called = false;

    public function action() {
        $this->called = true;
    }
}

Код для проведения этого теста тривиален и может быть легко проверен проверкой.

Мне любопытно, зачем использовать is_callable вместо method_exists, чтобы избежать создания массива?Вероятно, это просто личные предпочтения, но мне интересно, есть ли какие-то семантические различия.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...