Re[3]: Про юнит тесты растянем боян
От: IT Россия linq2db.com
Дата: 27.04.15 16:08
Оценка: 7 (2) +1
Здравствуйте, itslave, Вы писали:

IT>>Типичным индикатором такой ошибки, кстати, является использование моков.


I>хм, использование моков — это первый признак использования юнит тестов. Ну банальное там, операция "перевести деньги со счета на счет" подразумевает мокание базы данных и тестирования логики бизнес рулов перевода денег: там проверок что счета активные, денег достаточно и т.д. С твоей точки зрения — такие тесты делает что нибудь полезное?


Моки и юнит тесты — это две плохо совместимые вещи.

Юнит — это метод, класс, модуль, в идеале максимально независимый и самодостаточный. Идеальным примером юнита является функция, принимающая все входные данные как параметры и возвращающая результат работы как возвращаемые значения без создания каких-либо артефактов. Если сложность такого юнита не ограничивается одной функцией, то это может быть класс или даже целый модуль. Но главная идея остаётся — принимаем входные данные, какими бы сложными они ни были, возвращаем результат, каким бы печальным он ни оказался, не создаём при этом артефактов, даже самых несущественных.

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

Последнее следует пояснить. Лично я для себя выделяю несколько, принципиально отличающихся друг от друга, типов кода. Например, всевозможные структуры данных, DTO, AST, ADT и прочие контейнеры — это один тип кода, задача которого заключается в представлении данных. Тестирование такого кода в языках со строгой типизацией имеет исчезающе мало смысла и является признаком весьма бурно прогрессирующей паранойки. Второй тип кода — всё, что вне метода. Классы, сборки, неймспейсы, интерфейсы и т.п. Зачастую именно этот код определяет высокоуровневый дизайн приложения, но как его тестировать пока не придумали. Третий тип — алгоритмы, т.е. код, который делает работу. Как раз его в основном мы и тестируем.

Но есть ещё один тип кода — тот, который управляет кодом, который делает работу. При этом если код, который делает работу, делать максимально независимым и отчуждаемым, а управляющий код делать максимально примитивным, то появляется реальная возможность без проблем жонглировать кодом как вздумается. Передача юниту поведения убивает эту возможность наповал. В общем, coupling and cohesion в полный рост.

Кстати, что такое coupling and cohesion в теории знают все, а вот как с этим бороться на практике объяснить мало кто может. Так вот разделение кода на типы и примитивизация управляющего кода — это самый простой способ. Независимые, самодостаточные, легко отчуждаемые юниты не представляют угрозу увеличения сложности приложения, а вот управляющий код является чуть ли не фундаментальной основой большинства подобных проблем.

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

Можно ли без моков обойтись всегда? Конечно, нет. Но случаи их применения очень чётко ограничены — зависимость от неподконтрольных систем и длительное время работы внешнего компонента. Всё остальное косяки, особенно это касается моканья базы данных.

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