Как стать автором
Обновить

Комментарии 24

Код инициализации большей части зависимостей вынесен в общий класс.

Вот, по опыту, не стоит подобные вещи делать. Код тестов должен быть максимально "тупой", без всяких паттернов, повторного использования, самодельных библиотек и фреймворков и т.п. Чтобы в случае потери актуальности какого-то отдельного теста или набора тестов проще было его вообще полностью удалить и написать заново, чем рефакторить сами тесты.

 Чтобы в случае потери актуальности какого-то отдельного теста или набора тестов проще было его вообще полностью удалить и написать заново, чем рефакторить сами тесты.

Согласен. С другой стороны палка о двух концах. Если один и тот же код используется в разных тестах - легче вынести, поскольку в случае рефакторинга структурированный код отрефакторить значительно проще, чем в каждом тесте.

Просто получается что если все зависимости (семь в данном случае) мокать отдельно в тесте каждого хэндлера да ещё чтобы моки что-то вменяемое возвращали, количество копипаста убъёт возможности рекаторить эти зависимости... ((

НЛО прилетело и опубликовало эту надпись здесь

Надо будет посмотреть, но я заранее сомневаюсь, что там про то, как визиторы с декораторами в тестах использовать.

НЛО прилетело и опубликовало эту надпись здесь

Есть смысл расширять функцинальность и использовать новые сущности, которые конечно могут быть "подставлены", как нам велит измученный SOLID, тогда и тесты переписывать не нужно. В заголовке же Unit тесты.

В целом проблема известная.

Тесты приходится переписывать хотя бы при рефакторинге, когда, например, выносишь код в отдельный класс с зависимостью исходного класса от него.

НЛО прилетело и опубликовало эту надпись здесь

Цементирует не реализацию, а архитектуру, контракты. Понятно, что если рефактиринг это типа, а давайте заменим какой-нибудь медленный LINQ внутри на foreach, но что это за рефакторинг такой? Обычно в реализацию влазят только для исправления дефектов или оптимизация. Хороший добротный рефакторинг похреначит многие контракты и обязательно приведёт к переписыванию юнит-тестов.

Что интересно. Если при рефакторинге не приходится исправлять тесты -- признак очень хорошей архитектуры, ок. А зачем очень хорошую архитектуру подвергать вообще рефакторингу? :)

Пример, особенно при развитии системы, банальный. Вот был к примеру доступ на уровне команды, а появляется сущность "компания", и с правами менеджера можно смотреть по всем командам компании. И хорошие тесты позволят добавить пару проверок: тест на доступ менеджера компании и отрицательный кейс на несоответствие компаний. Остальные изменения только общие на инициализации базовых данных (явно репозитории и команды поменяются при введении новых сущностей).

Если юнит-тестами покрывать "богато", каждый кейс прописывать вручную отдельным тестом то при изменении доменной модели/репозитория придётся править каждый тест. Это я и называю "цементированием кода". Когда проще не менять, потому что иначе все тесты поломаются. А изменение тестов становиться очень дорогостоющим (из-за количества).

TDD точно не имеет смысл внедрять покрывая легаси

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

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

Хорошо спроектированные тесты (не знаю, попадает ли "куча" под это определение) должны проверять код на соответствие требованиям.

Для юнит-тестов это не так. Юнит тест проверяет то, что отдельный кусок кода работает в соответствие с тем, что имел в виду разработчик, когда его писал (белый ящик). Соответствие требованиям это уже тесты более высокого уровня.

НЛО прилетело и опубликовало эту надпись здесь

Тут вопрос только в том, что считать модулем. Если модуль это класс (как в случае юнит-тестов), то требования к нему почти никогда не детализируются до уровня нужного для написания этих самых юнит-тестов.

НЛО прилетело и опубликовало эту надпись здесь

Я немного поясню, потому что, возможно, я немного смутно выразился. "Полноценный" юнит-тест тестирует только код одного отдельного класса (в идеале - отдельного метода класса), подменяя всё что для этого класса/метода внешнее специальным образом сконфигурированными мок-объектами. А это означает, что сам этот юнит-тест будет уже зависеть не только от требований к этому классу/методу, но и от его реализации (т.е., как я уже говорил, "белый ящик"). Вот, например, у нас есть класс, который должен проверять регистрационные данные пользователя и записывать их в БД. В изначальной реализации мы работаем напрямую с ORM и для юнит-тестов используем in-memory database. А позже мы решили над ORM встроить еще один слой абстракции типа какого-нибудь IRepository - и тогда юнит-тесты уже надо будет модифицировать, хотя требования-то остались те же.

Сорри, у меня не осталось "плюсиков", но именно об этом я и говорю! Что при развитии системы надо менять базовые сущности/интерфейсы, а если на каждый кейс будет совершенно отдельный "простой"/"тупой"/"рукописный" тест, то количество работы по выравниванию тестов может "зацементировать" рефакторинг - он станет слишком дорогим...

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

>Покрытие тестами легаси кода даёт больше гарантии безопасности при изменениях и поддержке

Всё верно. Просто это точно не TDD ибо тут наоборот - сначала тест, а потом код и никак иначе! И по сравнению с TDD покрытие легаси unit-тестами не то что бесполезно, просто близко к пословице "скупой платит дважды" (один раз тестируя код руками без тестов, а позже покрывая его unit-тестами)

Смысл поговорки "скупой платит дважды" как раз в том, что скупой экономит сейчас, и в итоге потом переплачивает, что бы переделывать.

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

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

Не спорю. Ситуации разные. Если ситуация сначала пишем код, потом тесты - получается платим дважды. Но если ситуация что уже есть код а тестов нет, и код надо изменить, то конечно без тестов никуда, иначе попадём в ситуацию "скупой платит трижды". Просто чтобы там получилось TDD надо сначала, не трогая код, покрыть всё зелёными тестами. И только потом можно начинать делать тест красным в месте изменения.

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

Согласен что использовать тесты эффективней, чем искать и фиксить баги без них. Разрабатывать по TDD я считаю ещё более эффективной практикой.
В статье транслирую мысль, что слишком много тестов тоже могут негативно повлиять - усложнить дальнейшее развитие. Так что я не против тестов - я за их разумное использование! =)

Тесты служат предохранителем от произвольного изменения кода, своего рода защита от дурака.
Если оставлять какой-то код без тестов то в будущем как раз будут снова и снова возникать проблемы, и вот тут и будут выходить переплаты.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории