Comments 96
«Когда вы делаете что-то, не имея иных причин к этому, кроме “в крутой компании делают так ” — вы становитесь адептом культа карго.» т.е. Обычно имеется в виду формальное применение тех или иных методологий без понимания соответствующих процессов.
“У тихоокеанских островитян есть религия самолетопоклонников. Во время войны они видели, как приземляются самолеты, полные всяких хороших вещей, и они хотят, чтобы так было и теперь. Поэтому они устроили что-то вроде взлетно-посадочных полос, по сторонам их разложили костры, построили деревянную хижину, в которой сидит человек с деревяшками в форме наушников на голове и бамбуковыми палочками, торчащими как антенны — он диспетчер, — и они ждут, когда прилетят самолеты. Они делают все правильно. По форме все верно. Все выглядит так же, как и раньше, но все это не действует. Самолеты не садятся. Я называю упомянутые науки науками самолетопоклонников, потому что люди, которые ими занимаются, следуют всем внешним правилам и формам научного исследования, но упускают что-то главное, так как самолеты не приземляются.”
© Р. Фейнман, “Вы конечно шутите, мистер Фейнман”
Автор впал в другую крайность, имхо. "Я видел, как люди бьют себе молотком по пальцам, когда пытаются забить гвоздь. Вывод: молотки не нужны, да и гвозди тоже, от них одни проблемы."
Другой вопрос, зачем вообще разработчику врать...
А теперь представьте себе стэндап на котором "продакт" не присутствует. И "проводится" он абсолютно добровольно и даже иногда по нескольку раз в день. И называется это не "стэндап", а скажем "пошли покурим" или "пошли за кофе". Ничего не напоминает? :)
П.С. И если у вас кто-то не курит или не пьёт кофе, то он может тогда и выпасть из "системы". И тогда возможно имеет смысл хотя бы раз в день проводить официальный стэндап.
Вцелом идея статьи хорошая. Параллельно с увлечением средствами из глянцевых журналов идет игнорирование некоторых базовых этапов разработки. Например отсутствие этап проектировоания и соответственно документация по проектированию или же стандартизации.
Но вместе с этим по меногим пунктам не поддерживаю.
Ежедневные утренние обсуждения (aka Daily Standup)
Да. Если 10 разработчиков работают рядом и задачи имеют часто не пересекающиеся — утренний стендап/митап или как его там это только минус 5 часов суммарно (каждый день) или 107 часов в месяц.
Развертывание в Cloud
… Поэтому, чем проще описан проект — тем лучше, многим будет достаточно и Docker Compose.
cloud и docker-compose это разные вещи. Разворачивать продакшин проекты в облаке это просто удобно и зачастую дешевле чем на выделенном сервере. Но только если не на docker-compose. Применять docker-compose желательно там для чего он был разработан то есть для развертывания окружения разработчика или тестирования.
Покрытие кода unit-тестами
Если касается бэкенда покрытие желательно 100% чтобы быть уверенным что следующий коммит не сломал функионал бэкенда. Хотя я и не сторонник TDD. Что касается форнтенда, то хочу обратить внимание на Вашу фразу "Покрытие кода unit-тестами", подчеркнув при этом слово "unit". Если Вы разрабатываете компонент то его независимая от общего кода проекта разработка просто немыслима без написания unit-тестов (то есть тестирование отдельно от всего проекта).
Да. Если 10 разработчиков работают рядом и задачи имеют часто не пересекающиеся
Мне трудно представить эффективную команду, где 10 разработчиков не пересекаются. Что будет, если кто-то заболеет или уйдет в отпуск?
Разворачивать продакшин проекты в облаке это просто удобно и зачастую дешевле чем на выделенном сервере.
Как раз это в разы дороже, чем на выделенном сервере, если, конечно, не Hello World разворачивать. То, что удобно — соглашусь.
Если касается бэкенда покрытие желательно 100% чтобы быть уверенным что следующий коммит не сломал функионал бэкенда.
Вот сколько я коммерческих проектов не видел, ни в одном не было даже близко 100% покрытия.
Если Вы разрабатываете компонент то его независимая от общего кода проекта разработка просто немыслима без написания unit-тестов
В чем немыслимость, компоненты без unit-тестов невозможно создать или сопровождать?
Вот сколько я коммерческих проектов не видел, ни в одном не было даже близко 100% покрытия.
У меня в этом плане другой опыт.
В чем немыслимость, компоненты без unit-тестов невозможно создать или сопровождать?
Если у Вас задача разрбоать некий компонент, при помощи unit-тестов можно прогнать его с большим количетсвом входных параметров и потом выдать для встраивания в приложение. В противном случае нужно будет либо для него делать тестовую страничку, на которой все равно практически невозможно будет перебрать все возможные сочетания входных параметров.
Это занимает гораздо меньше времени, чем поиск багов и их починка, когда все начинает разваливаться из-за того, что изменения ломают какой-то другой функционал.
Любой большой SPA. На несколько десятков тысяч строк кода.
Вот пример большого SPA, хоть и весьма специфический: https://www.onshape.com
А мой опыт мне говорит о том, что вы либо троллите, либо не понимаете, о чем пишите, уж извините.
По моему троллите тут вы. А грамотное логирование действительно быстрее реализуется чем грамотный тест.
Но вы про SPA, да. Но там вообще нереально сделать 100% покрытия
PS. Если без тестов вы боитесь изменить строчку кода, потому что хрен знает где может выстрелить — значит с вашим проектом УЖЕ что то не так, и тесты это припарка.
А где я писал то, что вы мне приписываете?
Тесты нужны, прежде всего, для того, чтобы убедиться, что вы не поломали другие части системы, внося изменения куда-то. Как тут логгирование поможет?
И как вам поможет грамотное логгирование, если, например, релиз отгружен, развернут на серверах заказчика и тот начал спотыкаться о баги, которые можно было выловить тестами?
Кстати тесты «чтобы убедиться, что вы не поломали другие части системы» называются интеграционными.
И как вам поможет грамотное логгирование, если, например, релиз отгружен, развернут на серверах заказчика и тот начал спотыкаться о баги, которые можно было выловить тестами?
На практике автотесты ловят только самые простые баги(собсно потому и есть мнение что «да я лучше руками»). А логи — это, обычно, единственный способ понять, что происходит на «отгруженом, развернутом на серверах заказчика релизе» и если вы не понимаете как они помогают, то… Даже не знаю что и сказать.
кучу раз видел у вот таких вот адептов 100% покрытия
Вы тоже спорите с воображаемыми людьми у вас в голове? Можно цитату, где я писал, что я адепт 100% покрытия?
Кстати тесты «чтобы убедиться, что вы не поломали другие части системы» называются интеграционными.
Где я писал, что они называются как-то по-дугому? Цитату, пожалуйста.
и если вы не понимаете как они помогают
Они не помогают никак предотвратить ситуацию: релиз с багами на проде заказчика. Вот вообще никак. А тесты помогают.
Вот сколько я коммерческих проектов не видел, ни в одном не было даже близко 100% покрытия.
У меня в этом плане другой опыт.
Это же откровенная ложь, либо «опыт» был на «hello world» )
Не думал что еще существуют компании которые не делают прокрытия близкого к 100% по — хочу подчеркнуть — бэкэнду. А то в пылу комментов это уточнение ушло. Разве фриланс. Сделал и забыл.
Ололо no space on device )
Иногда надо не думать, а размышлять )
Что быстрее?
Написать
log.error('KAKAYATO HUINYA na stroke '+_stroka_+' v faile' + _fllename_)
Или написать тест со всеми возможными и невозможными входными данными?
И да, мы в реальном мире живем, а не с розовыми пони и разрабами пишущими тесты от души.
Но времени на них надо потратить прилично ) Не, я не против тестов. Если без фанатизма. Легаси какое нибудь покрыть и т.п.
P. S. Если вам наплевать на качество того, над чем вы рабртаете, то все совсем печально. Хреновый вы тогда специалист.
Видите, я тоже умею передергивать в вашей манере.
Помогают держать на уровне (если он был) но не более.
вообще тестами хоть один проект покрывали, кроме Hello World?Я всего лишь писал тонну rspec тестов в разных проектах, юниты, интеграционные, браузерные.
Еще раз, из практики — как я, так и другие программисты с которыми я работал — упрощали тестируемый код чтобы под него было проще писать тесты. Это работает.
Проще протестировать класс который умеет принимать аргументы в конструкторе или юзать DI чем плясать вокруг его неявных зависимостей которые просачиваются, после чего тест перестает быть юнитом, а становится интеграционным.
то должны понимать, что от «тяп-ляп» тесты не спасаютА я где то говорил что это панацея? я всего лишь говорю что написание юнитов стимулирует и тестируемый код писать чище. Потому что гавнокод сложнее тестировать.
что изменения ломают какой-то другой функционал
Если у вас в компонентном фронтэнде (SPA или не SPA — не важно) вдруг изменения в компоненте начинают кругом ломать неожиданный функционал — у вас такие проблемы, которые юнит-тестами мягко говоря не решаются.
Компонентное построение фронтэнда — оно как бы и нужно в том числе для того, чтоб в явном виде иметь описанные зависимости. Вы меняете контракт компонента? Всё, что использует этот компонент, будет сломано. Если у вас помимо этого ломается что-то еще — увольте своего главного по фронтэнду нафиг.
В противном случае нужно будет либо для него делать тестовую страничку, на которой все равно практически невозможно будет перебрать все возможные сочетания входных параметров.
А страничку всё равно надо будет делать, потому что юнит-тестами вы внешний вид не увидите. А у подавляющего большинства компонентов соль будет именно в нём.
Ну и если у вас компоненты с таким обилием параметров, что уже и комбинации не перебрать, и логика не охватывается одним взглядом — это верный признак того, что компонент давно уже пора разбивать.
Если касается бэкенда покрытие желательно 100%
А пустые геттеры и сеттеры — так вообще обязательно!
Чрезмерное увлечение тестами приводит к тому, что на это расходуются драгоценные ресурсы разработки,
Я так понимаю, что разработчики у вас просто пишут код и сразу заливают на тестовую среду? Ну, или вы предлагаете им тестировать локально руками?
Вы же понимаете, что тестировать руками куда дольше, чем тестировать при помощи написанного теста, при условии:
- Что вы умеет писать тесты
- Что вы не пишете идеально работающий код с первого раз и локальное тестирование нужно будет как минимум несколько раз прокликивать
Да и тесты это не только про TDD.
тестами все варианты не покроешь
В ручную то, конечно, все варианты проверяете. Тесты пишутся для типовых и пограничных вариантов.
Вы когда новую фичу добавляете вы только её тестите или проверяете не сломала ли эта фича что-то другое?
Или скажем у вас никогда не бывает критических изменений которые хотя бы теоретически могут повлиять на любой кусок программы? И что вы делаете в такой ситуации? Всё ручками тестите?
У нас автоматические тесты прогоняются каждую ночь и хотя бы раз в месяц какая-нибудь мелочь но ловится. И это при том что у нас пока 100% покрытия нет и покрыты действительно только более-менее важные компоненты. Но это потому что относительно много легаси кода и нет ресурсов ещё и им заниматься.
Вы когда новую фичу добавляете вы только её тестите или проверяете не сломала ли эта фича что-то другое?
Все зависит от важности фичи и возможного его влияния. Для backend-фич, обычно, после тестирования ее разработчиком и того, кто принимает результат, она попадает в staging environment, где тестируется различными методиками на интеграции, включая Black Box. Если все ОК — идет в production.
Важные фичи тестируются более тщательно, включая написание автотестов, если потребуется.
Или скажем у вас никогда не бывает критических изменений которые хотя бы теоретически могут повлиять на любой кусок программы?
В большинстве случаев баги будут отловлены через автотесты Black Box или QA. Если баг прошел мимо них, а не должен был — добавляем новые тесты или новые инструкции для QA(aka checklist).
нет ресурсов ещё и им заниматься
Как Вы правильно заметили, ресурсов всегда не хватает и правильное их распределение — это искусство управления, то есть вотчина тимлида и выше. Со временем правильно выстроенные процессы позволяют практически гарантированно отловить все критические баги, а не критические — допустимы.
Ну у нас ресурсов не хватает по одной простой причине: легаси кода много и он не особо хорошо покрыт автотестами. И произошло это потому что кто-то "умный" в своё время решил что не "надо покрывать всё автотестами и хватит только критические компоненты покрыть" .
И мы все этому человеку очень-очень "благодарны". Особенно тестеры и саппорт.
Что Вам мешает покрыть тестами этот легаси-код сейчас, если возникла в этом потребность?
Потихоньку покрываем. Вот только "клиент" нам за устранение наших ошибок платить не особо хочет.
Да и тесты намного проще писать сразу вместе с кодом пока ты ещё хорошо знаешь что конкретно этот код должен делать и почему он именно так написан.
Я бы не был так категоричен. Думаю убедить его можно было.
Да и сейчас его не то чтобы прямо спрашивали хочет он автотесты или нет.
Уверен, что клиент бы и первом этапе создания не захотел бы оплачивать 100% покрытие тестами
Я думаю он и выделение логики в отдельные классы не готов оплачивать и разделение приложения на слои тоже.
Что Вам мешает покрыть тестами этот легаси-код сейчас, если возникла в этом потребность?
На покрытие тестами кода, который не был покрыт тестами в процессе создания придётся потратить раз в 10 больше времени. Плюс его ещё придётся неплохо так отрефакторить.
- Я не сторонник TDD, но юнит тесты очень помогают разработке, позволяя в процессе написания (либо после написания) модуля найти возможные ошибки, которые там просто гарантированно будут (вряд ли вы прям так сходу пишете идеальный код). И с помощью сделать это проще, чем запуская руками, потому что руками вы фактически тестируете какую-то более сложную систему. Помимо этого в процессе написания теста еще раз очень внимательно смотрится код и можно найти еще какие-то проблемы.
- Тем не менее, помимо юнитов нужны интеграционные тесты, потому что по-кускам может работать, а в целом — нет. Тут, кстати, у меня есть непонимание, что считать юнитом. Вот какой-то юнит. Собрали несколько юнитов в юнит большего размера. И какой это будет тест? На проекте мы вообще отказались от подобного именования
- Coverage — это инструмент, может, полезный, но надо правильно интерпретировать результаты и он ничего не гарантирует. Приведенный ниже код имеет 100% line и branch coverage, но он неправильный, и тесты неполные. Нет ни проверки возможных ошибок, ни тестов на них.
int sum_values_in_file(const char* file, int* result)
{
FILE* f = fopen(file, "r");
int val;
*result = 0;
while (!feof(f)) {
fscanf(f, "%d ", &val);
*result += val;
}
return 0;
}
/* Test case #
File: "1 2 3 4 5"
Expected: return 0, result 15; */
юнит тесты очень помогают разработке, позволяя в процессе написания (либо после написания) модуля найти возможные ошибки
Никто ж не говорит, что unit-тесты всегда бесполезны и их нельзя применять. Это как один из инструментов для повышения качества продукта вполне эффективен, в определенных ситуациях и правильно написанные. Еще есть, как Вы правильно заметили, интеграционные тесты, а также Black Box и Input Validation тестирование, перекрестный code review, парное программирование, проверка кода специальными тулзами на ошибки — все это тоже повышает качество продукта. Естественно, за это нужно платить временем разработчиков/тестеров и другими ресурсами, что неизбежно. Для некоторых типов проектов, например, стартапов, это малоприменимо, так как нет столько времени на раскачку, а требования меняются чуть ли не ежедневно, что тянет за собой изменение тестов. Вполне работает для стабильных Enterprise-проектов с размеренными процессами и богатыми на ресурсы.
Никто ж не говорит, что unit-тесты всегда бесполезны и их нельзя применять.
Ну да, в основном говорят, что юнит тесты полезны в 90 процентах случаев.
Это как один из инструментов для повышения качества продукта вполне эффективен, в определенных ситуациях и правильно написанные.
Примерно в 90 процентах случаев этот инструмент эффективен, да.
Естественно, за это нужно платить временем разработчиков/тестеров и другими ресурсами, что неизбежно.
За отсутствие юнит тестов придётся серьёзно заплатить временем тестеров, разработчиков и другими ресурсами, тоже верно.
Для некоторых типов проектов, например, стартапов, это малоприменимо, так как нет столько времени на раскачку, а требования меняются чуть ли не ежедневно, что тянет за собой изменение тестов.
Странно, что вы не переживаете, что изменение требований влечёт за собой изменение документации, её, видимо, тоже лучше не писать. И ещё придётся поправить код методов, поэтому лучше на методы код не делить.
Граница юнита это его интерфейс. Когда две компоненты взаимодействуют это уже интеграционное тестирование.
Допустим у нас есть класс-контейнер, с возможностью добавлять удалять содержимое, может быть ограничен при создании, умеет говорить что он пуст и пр. Это его интерфейс. Чтобы убедиться в том, что контейнер ведет себя заявленным образом мы пишем юнит-тест. Если же у нас появляется класс управляющий несколькими контейнерами, то мы пишем интеграционный тест. Который будет и проверять правильно ли работает класс управляющий контейнерами под «нагрузкой».
Мы поменяем реализацию контейнера, поправим юнит-тесты, но понятия не будем иметь, что управляющая компонента теперь может начать работать неверно. А интеграционный тест это выявит.
Да, юнит-тест это атомарный уровень. Часто мы инициализируем кучу компонент для выполнения нашего юнит-теста, потому что зависимости™. И такой тест будет отчасти уже интеграционным тестом. Хотя инициализация зависимостей не является предметом теста, она будет проверяться неявным образом.
Интеграционный тест или юнит- зависит от охвата теста. Вот и все. Когда начинаешь осмысливать, что на самом деле проверяет твой тест, эти понятия становятся второстепенными.
Чтобы эффективно двигаться вперед по разработке, тестами нужно покрывать только действительно важные участки кода, где выполняются какие-то вычисления и диагностика вручную ошибок затруднена. Часто для стартапов покрытие тестами можно отложить до момента, когда структура кода станет стабильной, а бизнес-логика станет ясной и вряд ли существенно поменяется.
Суть юнит-тестов не в том, чтоб сложные вычисления проверить, а в том, чтоб гарантировать выполнение некоторых контрактов от твоего интерфейса. Чтоб потом когда вашу стартапную лапшу люди рефакторили, можно было понять, работает вообще код или нет. И если над проектом работает больше одного человека или даже один человек, но больше чем месяц, то шансов всё сломать без юнит-тестов гораздо больше, чем с.
Суть юнит-тестов не в том, чтоб сложные вычисления проверить, а в том, чтоб гарантировать выполнение некоторых контрактов от твоего интерфейсаЯ бы тут не согласился. Допустим функция вычисляет какую-нибудь налоговую ставку. На выходе она возвращает целочисленное значение. Делая на эту функцию юнит тест мы с одной стороны проверяем, что функция правильно рассчитывает эту ставку, на примерах. С другой стороны мы проверяем, что функция возвращает целочисленное положительное значение или ноль. Если эта функция так определена. Не надо спрашивать «писать ли юнит-тесты», надо спрашивать «что и как я хочу проверить». Юнит-тесты всего лишь инструмент.
Если же вы хотите сформулировать и проверить утверждения вроде «у женатого человека налоговая ставка всегда меньше, чем у неженатого», «если человек ИП, то налоговая ставка не превышает 8%», и так далее, то тесты тут не очень помогут.Это т.н. бизнес-правила, их тоже можно и нужно проверять автоматически. Тут зависит от реализации, если это все параметры одной функции, то можно тестировать функцию. Если это все разбросано по разным модулям, то тестируем сценарий через связку компонент. В некоторых системах вообще используются middleware с движком для бизнес-правил, в таких системах свои инструменты тестирования (см. Oracle Fusion, Oracle BPM).
Нужно самому решать на каком уровне тестировать. В конце концов можно сделать сквозной тест. Просто это самый трудозатратный способ. У сквозного теста полный охват, и поэтому он будет чувствителен к изменениям любых нас не интерсующих (в конретной точке проверки) деталей.
Другое дело, что невозможно с помощью тестов показать полную безошибочность программы. Это следствие теоремы Райса Но это не значит, что тесты нам не помогут.
За замечание про интерпретацию теоремы Райса спасибо. Намотал.
Исходя из вашей же собственной недавней статьи, лишь очень немногие языки обладают достаточно выразительной системой типов :)
Завтрак — это карго-культ. Стоишь у плиты 15 минут только чтобы получить эту стрёмную овсянку, со склизкой текстурой, которая еще говорят не полезна нифига и запиваешь растворимым Nescafe…
Это я к тому что то что описано в статье больше похоже на неумение в настройки этих инструментов (стэндап, юнит-тесты, итд). Понятно если на стэндапе говорят дословно "Вчера писал код, сегодня пишу код и завтра тоже планирую писать код, а, ну еще правлю такие-то баги" — это стрёмный стэндап. Но это не значит что тот стэндап у этой же команды не может быть нестрёмным. Наверное там где люди не рефлексирует на тему для чего им тот или иной процесс, хорошо ли он работает это может напоминать карго культ, как у этого CTO в статье.
Карго-культ в разработке ПО