Недавно я был TDDing заводским методом. Метод заключался в создании либо простого объекта, либо объекта, завернутого в декоратор. Декорированный объект может быть одного из нескольких типов, расширяющих StrategyClass.
В моем тесте я хотел проверить, соответствует ли класс возвращаемого объекта ожидаемому. Это легко, когда возвращается простой объект os, но что делать, если он помещен в декоратор?
Я пишу код на PHP, чтобы использовать ext/Reflection
класс обернутых объектов, но мне показалось, что он слишком усложняет вещи и несколько напоминает правила TDD.
Вместо этого я решил представить, getClassName()
что при вызове из StrategyClass будет возвращаться имя класса объекта. Однако при вызове из декоратора он возвращает значение, возвращаемое тем же методом в декорированном объекте.
Некоторый код, чтобы сделать его более понятным:
interface StrategyInterface {
public function getClassName();
}
abstract class StrategyClass implements StrategyInterface {
public function getClassName() {
return \get_class($this);
}
}
abstract class StrategyDecorator implements StrategyInterface {
private $decorated;
public function __construct(StrategyClass $decorated) {
$this->decorated = $decorated;
}
public function getClassName() {
return $this->decorated->getClassName();
}
}
И тест PHPUnit
/**
* @dataProvider providerForTestGetStrategy
* @param array $arguments
* @param string $expected
*/
public function testGetStrategy($arguments, $expected) {
$this->assertEquals(
__NAMESPACE__.'\\'.$expected,
$this->object->getStrategy($arguments)->getClassName()
)
}
//below there's another test to check if proper decorator is being used
Моя точка зрения здесь: нормально ли вводить такие методы, которые не имеют другого применения, кроме как облегчить юнит-тесты? Почему-то мне это не кажется правильным.
Ответы:
Я думаю, нет, вы не должны ничего делать только потому, что это необходимо для тестируемости. Многие решения, принимаемые людьми, приносят пользу тестируемости, и тестируемость может даже быть основным преимуществом, но это должно быть хорошим проектным решением по другим достоинствам. Это означает, что некоторые желаемые свойства не поддаются тестированию. Другой пример - когда вам нужно знать, насколько эффективна какая-то подпрограмма, например, использует ли ваш Hashmap равномерно распределенный диапазон значений хеш-функции - ничто во внешнем интерфейсе не сможет вам этого сказать.
Вместо того, чтобы думать: «Получаю ли я правильный класс стратегии», «думаю» выполняет ли класс, который я получаю, то, что пытается проверить эта спецификация ? » Приятно, когда вы можете проверить внутреннюю сантехнику, но вам не нужно это делать, просто проверьте вращение ручки и посмотрите, есть ли у вас горячая или холодная вода.
источник
Я считаю, что иногда нужно немного переделать исходный код, чтобы сделать его более тестируемым. Это не идеально и не должно быть оправданием для того, чтобы загромождать интерфейс функциями, которые должны использоваться только для тестирования, поэтому модерация здесь является ключевым моментом. Вы также не хотите оказаться в ситуации, когда пользователи вашего кода внезапно используют функции интерфейса тестирования для нормального взаимодействия с вашим объектом.
Мой предпочтительный способ справиться с этим (и я должен извиниться за то, что не могу показать, как это сделать в PHP, поскольку я в основном пишу на языках стиля C), состоит в том, чтобы предоставлять «тестовые» функции таким образом, чтобы они не подвергается воздействию внешнего мира самим объектом, но может быть доступно производным объектам. Для целей тестирования я бы затем получил класс, который обрабатывал бы взаимодействие с объектом, который я на самом деле хочу протестировать, и чтобы модульный тест использовал этот конкретный объект. Пример C ++ будет выглядеть примерно так:
Тип производства класс:
Тест помощника класса:
Таким образом, вы, по крайней мере, находитесь в положении, когда вам не нужно показывать функции типа «тест» в вашем главном объекте.
источник
Несколько месяцев назад, когда я поставил свою недавно купленную посудомоечную машину, из шланга вышло много воды, я понял, что это, вероятно, связано с тем, что она была должным образом проверена на заводе, откуда она поступила. Нередко можно видеть монтажные отверстия и прочее на оборудовании, которое находится там только для тестирования на сборочной линии.
Тестирование важно, если нужно, просто добавьте что-нибудь для него.
Но попробуйте некоторые из альтернатив. Ваш вариант на основе отражения не так уж и плох. Вы можете иметь защищенный виртуальный метод доступа к тому, что вам нужно, и создать производный класс для тестирования и утверждения. Возможно, вы можете разделить свой класс и напрямую протестировать получившийся листовой класс. Скрытие метода теста с переменной компилятора в вашем исходном коде тоже вариант (я почти не знаю PHP, не уверен, возможно ли это в PHP).
В вашем контексте вы можете решить не проверять правильную композицию в декораторе, а протестировать ожидаемое поведение, которое должно иметь оформление. Это, возможно, сфокусировало бы внимание на ожидаемом поведении системы, а не на технических спецификациях (что паттерн декоратор покупает вам с функциональной точки зрения?).
источник
Я абсолютный новичок TDD, но, похоже, это зависит от добавляемого метода. Исходя из того, что я понимаю в TDD, предполагается, что ваши тесты в какой-то степени "стимулируют" создание вашего API.
Когда все в порядке:
Когда все не в порядке:
источник