Предположим, у меня есть класс Manager, производный от базового класса Employee , и у этого Employee есть метод getEmail (), который наследуется Manager . Должен ли я проверить, что поведение метода getEmail () менеджера на самом деле такое же, как и у сотрудника?
На момент написания этих тестов поведение будет таким же, но, конечно, в какой-то момент в будущем кто-то может переопределить этот метод, изменить его поведение и, следовательно, сломать мое приложение. Тем не менее, кажется немного странным по существу проверять отсутствие вмешательства кода.
(Обратите внимание, что тестирование метода Manager :: getEmail () не улучшает покрытие кода (или даже любые другие показатели качества кода (?)) До тех пор, пока Manager :: getEmail () не будет создан / переопределен.)
(Если ответ «Да», будет полезна некоторая информация об управлении тестами, которые разделены между базовым и производным классами.)
Эквивалентная формулировка вопроса:
Если производный класс наследует метод от базового класса, как вы выражаете (тестируете), ожидаете ли вы, что унаследованный метод:
- Вести себя точно так же, как сейчас делает база (если поведение базы меняется, поведение производного метода не меняется);
- Вести все время точно так же, как и базовый (если меняется поведение базового класса, меняется и поведение производного класса); или
- Ведите, однако, то, что хотите (вас не волнует поведение этого метода, потому что вы никогда его не вызываете).
Manager
класса из IMOEmployee
был первой серьезной ошибкой.Ответы:
Я бы взял здесь прагматичный подход: если кто-то в будущем переопределит Manager :: getMail, то именно разработчик несет ответственность за предоставление тестового кода для нового метода.
Конечно, это справедливо только если
Manager::getEmail
действительно имеет тот же путь кода , какEmployee::getEmail
! Даже если метод не переопределен, он может вести себя по-другому:Employee::getEmail
могли бы назвать некоторые защищенные виртуальные ,getInternalEmail
который будет переопределен вManager
.Employee::getEmail
может получить доступ к некоторому внутреннему состоянию (например, некоторому полю_email
), которое может отличаться в двух реализациях: например, реализация по умолчаниюEmployee
может гарантировать, что_email
всегдаfirstname.lastname@example.com
, ноManager
более гибко назначать почтовые адреса.В таких случаях возможно, что ошибка проявляется только в том случае
Manager::getEmail
, если реализация самого метода одинакова. В этом случае тестированиеManager::getEmail
отдельно может иметь смысл.источник
Я мог бы.
Если вы думаете «ну, это действительно только вызов Employee :: getEmail (), потому что я не переопределяю его, поэтому мне не нужно тестировать Manager :: getEmail ()», тогда вы на самом деле не тестируете поведение менеджера :: getEmail ().
Я бы подумал о том, что должен делать только Manager :: getEmail (), а не о том, наследуется он или нет. Если поведение Manager :: getEmail () должно возвращать то, что возвращает Employee :: getMail (), то это тест. Если поведение возвращает «pink@unicorns.com», то это тест. Неважно, реализовано ли оно по наследству или переопределено.
Имеет значение то, что, если это изменится в будущем, ваш тест поймает его, и вы знаете, что что-то либо сломалось, либо нуждается в пересмотре.
Некоторые люди могут не согласиться с кажущейся избыточностью в проверке, но я бы сказал, что вы проверяете поведение Employee :: getMail () и Manager :: getMail () как отдельные методы, независимо от того, наследуются они или нет. переопределяется. Если будущему разработчику необходимо изменить поведение Manager :: getMail (), ему также необходимо обновить тесты.
Мнения могут отличаться, хотя, я думаю, Диггер и Хайнци дали разумные основания для обратного.
источник
Не проверяйте это модульно. Сделайте функциональные / приемочные испытания.
Модульные тесты должны проверять каждую реализацию, если вы не предоставляете новую реализацию, придерживайтесь принципа СУХОЙ. Если вы хотите потратить немного усилий здесь, вы можете улучшить исходный юнит-тест. Только если вы переопределите метод, вы должны написать модульный тест.
В то же время, функциональное / приемочное тестирование должно удостовериться, что в конце дня весь ваш код делает то, что он должен, и, мы надеемся, поймает любую странность из наследования.
источник
Правила Роберта Мартина для TDD :
Если вы будете следовать этим правилам, вы не сможете получить возможность задать этот вопрос. Если
getEmail
метод должен быть проверен, он был бы проверен.источник
Да, вы должны тестировать унаследованные методы, потому что в будущем они могут быть переопределены. Кроме того, унаследованные методы могут вызывать виртуальные методы, которые переопределяются , что может изменить поведение не переопределенного унаследованного метода.
Я проверяю это путем создания абстрактного класса для проверки (возможно, абстрактного) базового класса или интерфейса, например, (в C # с использованием NUnit):
Затем у меня есть класс с тестами, специфичными для
Manager
, и объединяю тесты сотрудника следующим образом:Причина, по которой я могу это сделать, - это принцип подстановки Лискова: тесты по-
Employee
прежнему должны проходить при прохожденииManager
объекта, поскольку он является производнымEmployee
. Поэтому я должен написать свои тесты только один раз, и могу убедиться, что они работают для всех возможных реализаций интерфейса или базового класса.источник
setUp()
! И почти каждая веб-инфраструктура MVC, которая включает в себя переопределение «индексного» метода, также ломает LSP, который в основном все из них.class ManagerTests : ExployeeTests
? (т. е. отражает наследование тестируемых классов.) Это в основном эстетика, чтобы помочь с просмотром результатов?Я бы сказал «нет», так как, по моему мнению, это будет повторный тест, который я бы проверил один раз в тестах «Сотрудник», и это будет так.
Если метод переопределен, вам потребуются новые тесты для проверки переопределенного поведения. Это работа человека, реализующего основной
getEmail()
метод.источник
Нет, вам не нужно тестировать унаследованные методы. Классы и их тестовые случаи, которые полагаются на этот метод, в любом случае сломаются, если поведение изменилось в
Manager
.Подумайте о следующем сценарии: адрес электронной почты собирается как Firstname.Lastname@example.com:
Вы проверили это на модуле, и он отлично работает для вас
Employee
. Вы также создали классManager
:Теперь у вас есть класс,
ManagerSort
который сортирует менеджеров в списке на основе их адреса электронной почты. Вы полагаете, что генерация электронной почты такая же, как вEmployee
:Вы пишете тест для вашего
ManagerSort
:Все отлично работает Теперь кто-то приходит и переопределяет
getEmail()
метод:Теперь, что происходит? Ваш
testManagerSort()
потерпит неудачу , потому чтоgetEmail()
изManager
было преодолено. Вы будете расследовать эту проблему и найдете причину. И все без написания отдельного тестового примера для унаследованного метода.Следовательно, вам не нужно тестировать унаследованные методы.
В противном случае, например, в Java, вам придется тестировать все методы, унаследованные от
Object
liketoString()
иequals()
т. Д. В каждом классе.источник