Андрей Коломенский
@onedev_link
Founder, Executive Partner at LeadStartup
Information
- Rating
- Does not participate
- Location
- Москва, Москва и Московская обл., Россия
- Date of birth
- Registered
- Activity
Founder, Executive Partner at LeadStartup
Information
Держим дизайн системы под контролем, используя изолированное юнит-тестирование
Держим дизайн системы под контролем, используя изолированное юнит-тестирование
Название должны ясно выражать намерения. Вы можете привести пример из класса, состоящего из 3-4-5 зависимостей, я сделаю из него класс с 1-2 зависимостями, причем они будут лучше выражать намерения чем те зависимости. Есть исключения, многопоточность, Observer-ы, но в целом задача решается.
Держим дизайн системы под контролем, используя изолированное юнит-тестирование
Если есть тесты, но они интегрированные, коротко алгоритм следующий:
Вижу что не могу написать изолированный тест — слишком много зависимостей. Просто беру и повышаю уровень абстракции, одновременно лучше выражая намерения за счет качественного подбора имени и названий методов. Зависимостей становится меньше, становится возможным написать изолированный тест.
Если в модуле больше 3-4 зависимостей, очень вероятно нарушение SRP. Наиболее вероятный алгоритм действий — прояснять выражаемые зависимостями намерения благодаря объединению нескольких раздельных зависимостей под одним понятием.
Тем самым мы:
Иногда, особенно если используется сервис-локатор, происходит просто взрыв новых, самых разнообразных классов. Просто аккуратно распихиваем ответственности по своим местам для SRP и OCP.
Есть много нюансов. В частности DDD помогает лучше сформировать выражения намерений, а также еще хочется добавить что наибольший результат этот подход дает для продуктов которые уже нашли свою бизнес–модель. Стартапам не нужно поддерживать продуктивность разработки на постоянном уровне.
Держим дизайн системы под контролем, используя изолированное юнит-тестирование
Держим дизайн системы под контролем, используя изолированное юнит-тестирование
Это тяжело. Это очень тяжело. И это профессионально.
> Есть какие-то аргументы в пользу этой точки зрения?
Опыт индустрии за последние 25 лет. Попробуйте пройти bowling kata game, это будет как один из примеров как это работает.
Держим дизайн системы под контролем, используя изолированное юнит-тестирование
Обычно так: «Для того чтобы правильно имплементировать это требование нужно изменить структуру 2х модулей. Один модуль связан с адресацией, второй с контрактами. Есть интеграционные тесты, но они тестируют только ограниченное количество вариаций. Если я где-то накосячу, а QA не обнаружит дефект то у компани из-за меня будут проблемы, очень вероятно финансовые, а мне это не нужно. Сделаю-ка я чуть-чуть по-другому.»
Альтернативное мышление в виде «Я проведу рефакторинг, а QA пусть ищет дефекты» не лучше.
>> И все попытки устранения, которые я видел, приводили лишь к общему снижению качеству архитектуры. Хотя, да, юнит-тесты в итоге писать было проще.
Если стало проще писать тесты, значит стало проще поддерживать систему в корректном состоянии и обеспечивать безопасный рефакторинг. Единственное исключение, при котором это не является преимуществом — ранняя валидация бизнес–модели.
В западных странах в нормальную компанию на позицию Senior бекэнд разработки не возьмут без навыков написания юнит-тестов. В Silicon Valley это ~99% компаний практикующие гибкие подходы к разработке, большая часть также использует TDD.
Держим дизайн системы под контролем, используя изолированное юнит-тестирование
Как вы будете производить рефакторинг, не имея уверенности что вы не внесете дефект? Как ваша команда будет безопасно интегрировать изменения и без страха устранять любые моменты плохо выражающие требования? Что позволит вам набраться храбрости на серьезное изменение структуры, если вы вдруг обнаружите что она не оптимальна?
Я уверен, существуют исключения, но как правило в среде, где рефакторинг означает «я поменяю структуру системы без изменения поведения, и возможно внесу в систему явные и скрытые дефекты» вынуждает разработчиков обходить проблемные места стороной, особенно под давлением сроков. Результат — загнивание системы и замедление разработки.
Проверьте, попробуйте написать юнит-тест на ваш текущий модуль, с полной изоляцией зависимостей. Любые сложности с которыми вы столкнетесь, будут связаны с одним из этих свойств. Обычно жесткость (сильная связанность) будет основной проблемой.
Держим дизайн системы под контролем, используя изолированное юнит-тестирование
Держим дизайн системы под контролем, используя изолированное юнит-тестирование
Держим дизайн системы под контролем, используя изолированное юнит-тестирование
Вы правильно говорите, нужно двигаться по циклу Red — Green — Refactor.
Как платить налоги и взносы ИП или зачем мы сделали бота-бухгалтера в Telegram
Вы даже не формируете платежки и никак не помогаете с отправкой отчетности.
Я не говорю, что если чуть расширю свою деятельность, добавится патент или сменю СНО или появятся сотрудники и т.п. Перечисленные вами облачные бухгалтерии легко и быстро сделают доработку на подсказку когда лучше платить взносы (а я знаю что ребята-разрабы из этих сервисов мониторят конкурентов), а вот чат-бот до этих сервисов не вырастет никогда. Ведь ценность сервисов как раз в том, что они полностью закрывают мою потребность в бухгалтере и не требуют от меня знания налогового и бух. учета, т.к. все операции автоматически разбираются в зависимости от типа и моей СНО и других особенностей.
Кроме того, налоговое планирование само по себе имеет смысл только при рассмотрении каждой конкретной ситуации. Для этого работает служба сопровождения клиентов, в т.ч. онлайн-конультант и Служба консалтинга, которые по запросу клиента могут предложить ему наиболее «эффективный» вариант, в том числе и по учету взносов в уменьшение налога, и в целом по организации бизнеса. (В мое дело точно работает, насчет эльбы — вроде, но не уверен)
Кроме того в Мое дело и эльбе рассчитаны суммы налогов и лишь предложены, а редактировать их можно вдоль и поперек, если так хочется и создавать платежки + сразу отправлять в свой банк по интеграции.
Т.е. выдернули какой-то отдельный кейс и вокруг него учинили целый чат-бот. Да и в этом кейсе все настолько условно… присоединюсь к мнению выше — ну а если доходы будут до конца года? Тогда получается вычет уже исчерпан. Т.е. в других сервисах нагрузка равномерная с учётом того, что в общем случае ИП ведёт деятельность и получает доходы.
Спасибо, можно не отвечать на комментарий.
О Legacy-коде без максимализма: что делать
Есть несколько определений legacy code. Одно приведено в статье: «Код не покрытый юнит-тестами». Мне ещё нравится другое определение: «Код, который страшно изменять».
Если использовать первое определение легаси кода то TDD является именно что серебряной пулей, потому что изначально не появляется никакого не покрытого тестами поведения.
Костыли появляются потому что в систему становится страшно вносить изменения. Полностью покрытая юнит-тестами система убивает этот страх и вместо костылей вы получаете возможность проводить нормальный рефакторинг. Полностью покрыть юнит-тестами систему, сделать её тестопригодной, легче и лучше всего с помощью TDD.
Можете привести примеры? Обратная связь от кода наоборот должна помогать построить правильный дизайн системы.
Michael C. Feathers: “Code without tests is bad code. It doesn't matter how well written it is; it doesn't matter how pretty or object-oriented or well-encapsulated it is. With tests, we can change the behavior of our code quickly and verifiably. Without them, we really don't know if our code is getting better or worse.”
Тоже хочу пример. Любой. Обычно, TDD ускоряет и разработку и внесение изменений. Интересно послушать другие кейсы.
Статья прям пропитана влиянием Дяди Боба :)
Записки правдивого архитектора: просто о самом главном (Ч.2)
Можете рассказать как вы измеряете качество архитектуры?
Детали test-first, которых так не хватало
Верно.
Вот кстати я не до конца понимаю этот момент. Если мы мочим реализацию public методов зависимостей, которые потом использует
тест, это считается black box или нет? С одной стороны мы предполагаем что должно происходить внутри метода, с другой стороны нас это не интересует, так как нам важно получить нужный assert и не важно как метод это будет делать.
Детали test-first, которых так не хватало
1. Не разумно выносить в функцию? Вам необходимо вводить отдельный класс, обязанностью которого будет осуществление рассылки и подключать его как зависимость.
2. Не единственное: тесты тоже будут использовать этот метод. Это также аргумент в сторону тех кто не хочет создавать интерфейсы для только одной реализации. Stubs тоже будут использовать этот интерфейс.
Если вы пишите тесты перед реализацией то реализация представляет собой чистейший черный ящик, потому что реализации ещё нет.
Детали test-first, которых так не хватало
Детали test-first, которых так не хватало
Нет. Рефакторинг подразумевает изменение структуры программы без изменения её поведения. На стадии рефакторинга вы не можете писать новые тесты по определению. Настоящая работа происходит на всех этапах.
Сразу после того как вы видите дублирование или возможность для упрощения дизайна системы. Все тесты в начале и процессе рефакторинга, разумеется, должны всегда проходить.
Не проще, потому что у вас дедлайн и вам платят за разработку ценного функционала.
TDD это строгая дисциплина и она требует определенного последовательного процесса работы. Я сам прошел через это. «у меня метод уже полностью в голове реализован и он точно будет корректен, зачем писать эту пачку бесполезных тестов?». Лень. Медленные интегрированные тесты, когда желания писать много тестов нет вообще. И минимальная реализация с хардкодом при первом тесте, это же для дураков ведь, да? Я же умный, могу писать правильную реализацию сразу и очнуться через пол-часа с десятью интегрированными тестами, которые не принуждают дизайн системы быть удобным для тестирования, не дают по нему никакой обратной связи и не отлавливают все ошибки в коде. Зато «сэкономил» 15 минут, о да.
Детали test-first, которых так не хватало
Почему бы не фиксировать одно требование за раз?
черт, промазал
Детали test-first, которых так не хватало
У вас выбор: написать if в зависимости от входного значения, либо написать функцию сложения. Тут надо опять же руководствоваться принципом минимальной реализации. Написать функцию сложения проще чем бесконечно удовлетворять условиям однотипных тестов.
Помните, вы пишите тесты перед реализацией для того чтобы облегчить себе жизнь. Вам не нужно тестировать в стиле «если функция работает с (1,1) то не факт что она будет работать с (100,100), напишу ка я тест». Вы понимаете что вам для удобства и удовлетворения требований бизнеса необходимо от метода конкретное поведение. Вы его фиксируете тестом. Если хотите протестировать ещё и пограничные состояния вы делаете ставку и пишите тест. Если тест в будущем сломается вы выиграли. Если нет, то тест оказался бесполезным. В любом случае тест должен обязательно провалиться, иначе это стопроцентно бесполезный тест.
Детали test-first, которых так не хватало
Ещё один момент: чтобы не словить ситуацию когда вы подбираете и бесконечно пишите бесполезные тесты вроде (1,0), (1,1), (1,2) и т.д, тест после написания обязательно должен провалиться (второй принцип TDD). Если тест после написания не проваливается значит он не нужен.