Пять ножей в спину Unit-тестов
May. 6th, 2013 08:03 pmВ формате надписей на заборе
1. Dependency Injection is evil
2. DI Containers (aka Inversion of Control Containers - blablabla buzzwords) suck
3. Mock objects considered harmful
4. Test Driven Development is bad
5. Unit Testing "best practices" are more kind of fanatic religious destructive cargo-cult than "technology" or even "craft".
----
That is, we all want to make gold, just don't tell me you can make it from shit.
1. Dependency Injection is evil
2. DI Containers (aka Inversion of Control Containers - blablabla buzzwords) suck
3. Mock objects considered harmful
4. Test Driven Development is bad
5. Unit Testing "best practices" are more kind of fanatic religious destructive cargo-cult than "technology" or even "craft".
----
That is, we all want to make gold, just don't tell me you can make it from shit.
no subject
Date: 2013-05-07 03:46 am (UTC)Почему? Я его продуктивно использую, даже когда никаких юнит тестов и близко не пишу.
2. DI Containers suck
Почему?
3. Mock objects considered harmful
Кому от них плохо?
4. Test Driven Development is bad
Ну, зависит. "Bad" - то вообще как-то субъективно очень. Один скажет "bad", другой - "good", а кто прав? Но продуктвиность при строгом религиозном ТДД может страдать, да. Особенно, когда твой код зависит от внешних систем, которые не совсем понятно как будут работать, т.е. в 80% случаев. Я в большинстве случаев пишу тесты ПОСЛЕ написания основного кода, или по крайней мере его костяка.
5. Unit Testing "best practices" are... destructive
Это инструмент. Если его использовать не по делу, он будет destructive. Как, скажем, молоток.
no subject
Date: 2013-05-07 12:08 pm (UTC)> Почему? Я его продуктивно использую, даже когда никаких юнит тестов и близко не пишу.
Потому что нарушают инкапсуляцию, заставляя верхние уровни входить в детали имплементации тех объектов, что они используют.
Есть большая разница между "если объект А вызывает методы объекта B, то объект B ему зачастую лучше передавать в виде интерфейса IB, а не непосредственно инстанса типа B" что обычно полезно в обычном кодировании и дизайне и "если объект A использует объект B, то объект B нужно вырвать из его кода со всеми кишками и передавать его интерфейс (или интерфейс его фактори) снаружи". Второе, а именно оно и есть dependency injection, приводит к спаггети-коду, который 30% своего текста занимается созданием и передачей во все стороны и на всевозможные глубины всевозможных аргументов instances & factories. При этом нарушается связность кода (понять кто кого вызывает становится отдельной интеллектуальной задачей, задача чтения кода реально усложнется), резко возрастает количество передаваемых параметров в функции и конструкторы, верхним уровням, как уже было сказано, приходится входить в детали реализации нижних.
Принцип dependency-injection доведенный до совершенства в рамках unit-testing - это написать класс, использующий мютекс, два кондвара, вектор, map и три интеджера и всё это передавать ему снаружи в виде интерфейсов или factories. И если по дороге в этот ад кто-то останавливается раньше - это не меняет сути.
no subject
Date: 2013-05-07 06:29 pm (UTC)Это только если доводить систему до абсурда, передавая map-ы. А чаще получается только удивительная гибкость. Типа о, так пчелкам же можно вот такой хитрый мёд-ридер передать вместо обычного, а пчлеки ничего и не заметят. В то время как "new" по определению не полиморфен; если пчелки хотят добывать мёд из дупла, они будут добывать его именно из дупла и ниоткуда больше.
заставляя верхние уровни входить в детали имплементации
Обычно верхние уровни ни в какие детали не вдаются. Они просто говорят "хочу пчелок". Типа,
[Dependency] public IBeeCollection Bees {get; set;}А пчелки говорят "хочу источник меда". Инкапсуляцию нарушает только "сборщик", который из всего этого хлама собирает работающую конструкцию. Это такой заповедник для операторов "new". Впрочем, сборщик как правило более-менее автоматический и тоже ничего не нарушает.
приводит к спаггети-коду, который... занимается созданием и передачей во все стороны... instances
Так это вы их готовить не умеете. DI Frameworks умеют много гитик. Ничего никуда передавать не надо. Каждый кусок зависит ровно от одного инстанса, он же фактори - от DI контейнера. Которому он говорит "дай". Либо в явном виде, либо (чаще) через какие-нибудь атрибуты/аннотации, а то и просто по конвенции. Мол, если есть конструктор с параметром
IFoo, то его надо поискать в контейнере. Более того, автоматические моки тоже бывают.Принцип dependency-injection доведенный до совершенства
Принцип молотка, доведенный до совершенства - это разбитые окна, вмятины в полу и стенах и проломленная голова. И если по дороге в этот ад кто-то останавливается раньше - это не меняет сути. Молотки - вредны. :)
no subject
Date: 2013-05-08 12:06 am (UTC)А так да, мой опыт относится к С++, да и там довольно ограничен.
Что, в принципе, не мешает мне подозревать, что всякие там навороченные фреймоворки с рефлекшеном стоящие на службе этого вашего волшебного dependency injection - тоже лажа. Я, кстати, это уже обозначил.
DI Containers suck.
no subject
Date: 2013-05-08 12:09 am (UTC)возражение принимается.
no subject
Date: 2013-05-07 06:34 pm (UTC)Тогда это действительно байтораздирающее зрелище. Но это не DI кривые - это C++ кривой. Потому что в нем reflection нет.