Лучшие практики для тестирования защищенных методов с [закрытым] PHPUnit

275
задан Community 23 May 2017 в 02:10
поделиться

6 ответов

Если вы используете PHP5 (> = 5.3.2) с PHPUnit, вы можете протестировать свои частные и защищенные методы, используя отражение, чтобы сделать их общедоступными перед запуском тестов:

protected static function getMethod($name) {
  $class = new ReflectionClass('MyClass');
  $method = $class->getMethod($name);
  $method->setAccessible(true);
  return $method;
}

public function testFoo() {
  $foo = self::getMethod('foo');
  $obj = new MyClass();
  $foo->invokeArgs($obj, array(...));
  ...
}
403
ответ дан 23 November 2019 в 02:08
поделиться

Вы, кажется уже, знаете, но я просто вновь заявлю о нем так или иначе; это - плохой знак, если необходимо протестировать защищенные методы. Цель модульного теста, должен протестировать интерфейс класса, и защищенные методы являются деталями реализации. Однако существуют случаи, где это имеет смысл. При использовании наследования Вы видите суперкласс как обеспечение интерфейса для подкласса. Таким образом, здесь, необходимо было бы протестировать защищенный метод (Но никогда частный один). Решение этого, состоит в том, чтобы создать подкласс для тестирования цели и использовать это для представления методов. Например:

class Foo {
  protected function stuff() {
    // secret stuff, you want to test
  }
}

class SubFoo extends Foo {
  public function exposedStuff() {
    return $this->stuff();
  }
}

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

47
ответ дан troelskn 23 November 2019 в 02:08
поделиться

Я думаю, что troelskn близок. Я сделал бы это вместо этого:

class ClassToTest
{
   protected testThisMethod()
   {
     // Implement stuff here
   }
}

Затем реализуйте что-то вроде этого:

class TestClassToTest extends ClassToTest
{
  public testThisMethod()
  {
    return parent::testThisMethod();
  }
}

Вы тогда запускаете свои тесты против TestClassToTest.

должно быть возможно автоматически генерировать такие дополнительные классы путем парсинга кода. Я не был бы удивлен, предлагает ли PHPUnit уже такой механизм (хотя я не проверил).

10
ответ дан Sliq 23 November 2019 в 02:08
поделиться

Я предлагаю следующий обходной путь для "Henrik Paul" s обходной путь / идея :)

Вы знаете имена частных методов вашего класса. Например, они похожи на _add (), _edit (), _delete () и т. Д.

Следовательно, когда вы хотите протестировать его в аспекте модульного тестирования, просто вызывайте частные методы, добавляя префиксы и / или суффиксы некоторых общих слово (например, _addPhpunit), чтобы при вызове метода __call () (поскольку метод _addPhpunit () не существует) класса владельца, вы просто помещали необходимый код в метод __call () для удаления префикса / суффикса слова / s (Phpunit), а затем вызвать оттуда выведенный частный метод. Это еще одно хорошее применение магических методов.

Попробуйте сами.

2
ответ дан 23 November 2019 в 02:08
поделиться

Вы действительно можете использовать __call() в общем случае для доступа к защищенным методам. Чтобы иметь возможность протестировать этот класс

class Example {
    protected function getMessage() {
        return 'hello';
    }
}

вы создаете подкласс в ExampleTest.php:

class ExampleExposed extends Example {
    public function __call($method, array $args = array()) {
        if (!method_exists($this, $method))
            throw new BadMethodCallException("method '$method' does not exist");
        return call_user_func_array(array($this, $method), $args);
    }
}

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

Теперь сам тестовый пример отличается только тем, где вы конструируете тестируемый объект, меняя ExampleExposed на Example.

class ExampleTest extends PHPUnit_Framework_TestCase {
    function testGetMessage() {
        $fixture = new ExampleExposed();
        self::assertEquals('hello', $fixture->getMessage());
    }
}

Я полагаю, что PHP 5.3 позволяет вам использовать отражение для изменения доступности методов напрямую, но я предполагаю, что вам придется делать это для каждого метода отдельно.

5
ответ дан 23 November 2019 в 02:08
поделиться

Я собираюсь бросить свою шляпу в кольцо здесь:

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

1: сгенерируйте stdClass или пользовательский класс (для принудительного применения типа)

2: заполните его требуемым методом и аргументами

3: убедитесь, что ваш SUT имеет метод acceptVisitor, который выполнит метод с аргументами, указанными в классе посещения

4: внедрить его в класс, который вы хотите протестировать

5: SUT введет результат операции в посетителя

6 : примените условия тестирования к атрибуту результата посетителя

5
ответ дан 23 November 2019 в 02:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: