Pull to refresh

Comments 14

Гораздо приятней было бы читать, если бы листинги были оформлены аккуратней, тесты визуально отделены от кода, проставлены комментарии, что делает код или даны более осмысленные названия.
А еще лучше был бы формат в виде советов с примерами, а не загадок, поскольку проблемных мест больше, чем указано )
Да, данная публикация прям наглядно показывает причину появления описанных в ней юнит тестов — не надо садиться писать статью уставшим в десять вечера.

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


А повысить качество написания тестов в команде удобнее на собственном примере, отталкиваясь от лучших практик, от того, как надо делать, а не как не надо. И обучалку-доклад-митап построить в виде "вот у нас есть такой кейз в нашем коде, и покрывать его тестами надо вот таким образом, как у меня (вот пермалинк на гитлаб). Это лучше, чем решение в лоб таймаутом по таким-то причинам." По крайней мере я так делал. С выкладыванием этого на конфлюенс. Что не отменяет короткого перечня рекомендаций по оформлению тестов)

Тестировать код с моками, всё равно что тестировать функцию A и надеяться что от этого абсолютно независимая функция B будет работать правильно. Не логичнее ли вместо моков делать интеграционные тесты, которые хоть и сложнее, но всё же хотя бы реальные? Иначе какой смысл говорить себе «допустим, что эта функция вернула мне из бд пользователя с таким-то id...», если на самом деле эта хрень обрушится ещё 10 раз самым неожиданным для вас образом. Либо же выносить внешние зависимости из чистого кода и передавать туда уже готовые для переваривания данные. И, как мне кажется, по возможности следует использовать property-based тесты, потому что я слабо представляю как можно написать несколько тест кейсов и надеяться, что это надёжно, хотя на самом деле это просто смешно.
Не логичнее ли вместо моков делать интеграционные тесты, которые хоть и сложнее, но всё же хотя бы реальные?

Логичнее отделять мух от котлет, т.е. делать юнит тесты с моками И интеграционные тесты с реальными связями.
Вы вот увидели упавший интеграционный тест, то как быстро вы можете получить ответ «В каком именно компоненте появился баг?». К примеру вы сейчас на совещании с командой и без открытой IDE и нет возможности запустить отладчик.

Тесты с моками(не стабами) говорят о том, что на том или ином наборе компонент действительно ведет себя тем или иным способом. Позвал 5 раз функцию1, 15 раз функцию3 и др. Выполняется этот тест быстрее чем интеграционый. В разы быстрее!

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

При тестах с моками можно увереннее говорить «Ошибка в компоненте1» или «Ошибка работы компонента2 в методе3 при пустой строке». Да, конечно что-то вы не сможете получить сразу, т.к. ненаписан тест. Но ведь все же итеративно!

Увидели багу, добавили тест. Поправили багу, зарефакторили, запустили тесты. Все ок? Вперед к другой задаче. Тест больше не нужен, т.к. уже не актуален после рефакторинга? ОК, выпилем его. Это же до боли привычный мир разработки
Это пример формальных тестов.
Вроде он есть, а на самом деле — нет.

А если серьёзно, то проспустил при создании репозитория. Обнаружил на этапе написания статьи и решил оставить пасхалочкой для внимательных. Смайлик.
Пункты 4 и 14 противоречат друг другу.

Сначала говорится, что если результат `require()` нужен только одному тесту, то его не нужно выносить наверх. Затем, спустя 10 пунктов, это объявляется антипаттерном и предлагается избавиться от `require()` в теле тестов.
Да, не совсем удачный пример в 4, поправлю.
Дело в том, что тестовые данные по хорошему нужно грузить не через `requiire`, а через стандартное чтение файла с парсингом. Иначе вы всё равно навсегда оставите его в памяти и сделаете потенциально мутируемым.

Так что в примере должно было быть чтение файла, а не `require`.

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

А есть какие-нибудь подтверждения, что такая загрузка данных локально в тесте вообще хоть что-то ускоряет?
По моей информации, V8 очень хорошо вычищает неиспользуемые значения из замыкания, и дополнительно помогать ему не нужно.


И ещё вопрос: откуда в тестах берутся гигабайты данных? Для проверки бизнес-логики столько не надо. Поэтому совет #4 выглядит как предложение сэкономить на спичках

А есть какие-нибудь подтверждения, что такая загрузка данных локально в тесте вообще хоть что-то ускоряет?

Не ускоряет, а позволяет не тратить лишнюю память.


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

Так они очень даже используемые, и будут все подгружены в момент запуска тестов. Возможно, по мере прохождения тестов они и будут выгружаться — но вряд ли, да и поздно уже.


И ещё вопрос: откуда в тестах берутся гигабайты данных? Для проверки бизнес-логики столько не надо. Поэтому совет #4 выглядит как предложение сэкономить на спичках

Всё зависит от объёма проекта, количества тестов, и данных, которыми он занимается. Так что в большинстве случае это, конечно, будет экономия на спичках. Но почему бы так не делать, если это не заставляет предпринимать лишние усилия.

18.


Тест, который проверяет, что тестируемый метод работает строго определенным образом: обращается к другим методам в строго определенном порядке и не обращается ни к чему еще.

Sign up to leave a comment.

Articles