Если у меня есть функция в моем коде, которая выглядит следующим образом:
class Employee{
public string calculateTax(string name, int salary)
{
switch (name)
{
case "Chris":
doSomething($salary);
case "David":
doSomethingDifferent($salary);
case "Scott":
doOtherThing($salary);
}
}
Обычно я реорганизовал бы это, чтобы использовать Ploymorphism, используя фабричный класс и шаблон стратегии:
public string calculateTax(string name)
{
InameHandler nameHandler = NameHandlerFactory::getHandler(name);
nameHandler->calculateTax($salary);
}
Теперь, если бы я использовал TDD, у меня были бы некоторые тесты, которые работали бы с оригиналом calculateTax()
перед рефакторингом.
например:
calculateTax_givenChrisSalaryBelowThreshold_Expect111(){}
calculateTax_givenChrisSalaryAboveThreshold_Expect111(){}
calculateTax_givenDavidSalaryBelowThreshold_Expect222(){}
calculateTax_givenDavidSalaryAboveThreshold_Expect222(){}
calculateTax_givenScottSalaryBelowThreshold_Expect333(){}
calculateTax_givenScottSalaryAboveThreshold_Expect333(){}
После рефакторинга у меня будет класс Factory NameHandlerFactory
и как минимум 3 реализации InameHandler
.
Как мне провести рефакторинг моих тестов? Должен ли я удалить модульный тест для claculateTax()
from EmployeeTests
и создать класс Test для каждой реализации InameHandler
?
Должен ли я также проверить класс Factory?
salary
в функциюcalculateTax()
был добавлен. Таким образом, я думаю, я буду дублировать тестовый код для исходной функции и трех реализаций класса стратегии.Начну с того, что я не эксперт по TDD или модульному тестированию, но вот как я это протестирую (я буду использовать псевдоподобный код):
Поэтому я бы проверил, что
calculateTax()
метод класса employee правильно запрашивает егоNameHandlerFactory
для a,NameHandler
а затем вызываетcalculateTax()
метод возвращаемого значенияNameHandler
.источник
Employee.calculateTax()
методе. Таким образом, вам не нужно добавлять дополнительные тесты Employee, когда вы вводите новый NameHandler.Вы берете один класс (сотрудник, который делает все) и делаете 3 группы классов: фабрика, сотрудник (который просто содержит стратегию) и стратегии.
Итак, сделайте 3 группы тестов:
Конечно, вы можете делать автоматические тесты для всего shebang, но теперь они больше похожи на интеграционные тесты и должны рассматриваться как таковые.
источник
Прежде чем писать какой-либо код, я бы начал с теста для Фабрики. Подшучивая над тем, что мне нужно, я заставил бы себя задуматься о реализации и вариантах использования.
Затем я реализую Factory и продолжу тестирование для каждой реализации и, наконец, сами реализации для этих тестов.
Наконец я бы удалил старые тесты.
источник
Мое мнение таково, что вы ничего не должны делать, то есть не должны добавлять новые тесты.
Я подчеркиваю, что это мнение, и оно на самом деле зависит от того, как вы воспринимаете ожидания от объекта. Как вы думаете, пользователь класса хотел бы предоставить стратегию для расчета налога? Если ему все равно, то тесты должны отражать это, а поведение, отраженное в модульных тестах, должно заключаться в том, что им не нужно заботиться о том, чтобы класс начал использовать объект стратегии для расчета налога.
Я действительно сталкивался с этой проблемой несколько раз при использовании TDD. Я думаю, что основная причина в том, что объект стратегии не является естественной зависимостью, в отличие от, скажем, архитектурно-граничной зависимости, такой как внешний ресурс (файл, БД, удаленный сервис и т. Д.). Поскольку это не естественная зависимость, я обычно не основываю поведение своего класса на этой стратегии. Мой инстинкт состоит в том, что я должен менять свои тесты, только если ожидания от моего класса изменились.
Есть отличный пост от дяди Боба, в котором рассказывается именно об этой проблеме при использовании TDD.
Я думаю, что тенденция тестировать каждый отдельный класс - это то, что убивает TDD. Вся прелесть TDD в том, что вы используете тесты для ускорения разработки схем, а не наоборот.
источник