Я использовал тест-управляемую разработку с последующим ре-факторингом и получил набор классов, использующих композицию для постепенного повышения специфичности типов. Например:
- EventDispatcher -> TypedEventDispatcher -> FooEventDispatcher
- EventDispatcher -> TypedEventDispatcher -> BarEventDispatcher
Таким образом, EventDispatcher отправляет общие события (String, Number, Object), TypedEventDispatcher отправляет только события, основанные на объектах, а FooEventDispatcher только отправляет события Foo, BarEventDispatcher отправляет события Bar и так далее.
Тесты для всех классов имеют идентичное поведение, единственное отличие - тип объекта, с которым проверяется. Я обнаружил, что когда я хочу добавить новый тип события, я получаю тот же тестовый код, что и для других классов.
Это количество дублирования явно неверно. Получающиеся в результате тесты довольно хрупкие, поскольку, когда я добавляю новую функцию в TypedEventDispatcher, мне нужно добавить те же тесты для любых объектов, которые ее используют.
В Java, .NET или Haxe я хотел бы создать универсальный тип и протестировать его, однако я использую ActionScript, который не поддерживает обобщенные типы, поэтому мне нужен новый класс для каждого типа для обеспечения безопасности типов .
Насколько я вижу, мои варианты:
- Продолжайте создавать одинаковые тесты для каждого нового класса.
- Игнорируйте тесты для производных классов, просто создайте новые классы и предположите, что они работают.
Вариант № 1 представляется наиболее «правильным», но также приводит к хрупким тестам. Вариант № 2 имеет смысл в том, что базовый объект (TypedEventDispatcher) тестируется, поэтому производные классы все еще должны работать.
Итак, мой вопрос: какова философия TDD для мотивации создания новых классов на основе композиции?
Обновление:
Чтобы задать вопрос по-другому, нормально ли в итоге получить дубликат тестового кода при получении функциональности из существующего кода?
Обновление:
Тест BaseEventDispatcher:
class BaseEventDispatcherTest {
protected function test_dispatchEvent(dispatcher:Object, event:*):void {
dispatcher.dispatch(event);
assertEventDispatched(event)
}
}
Тест TypedEventDispatcher:
class TypedEventDispatcherTest extends BaseEventDispatcherTest {
[Test]
public function dispatchEvent_TypedEvent_should_dispatch_event():void {
var event:TypedEvent = new TypedEvent();
test_dispatchEvent(dispatcher, event);
}
}
FooEventDispatcher test:
class FooEventDispatcherTest extends BaseEventDispatcherTest {
[Test]
public function dispatchEvent_FooEvent_should_dispatch_event():void {
var event:FooEvent = new FooEvent();
test_dispatchEvent(dispatcher, event);
}
}