🔥

Тред (Дима Коваленко)


Сегодня будем говорить про надежность наших тестов. И в связи с этим хочу спросить: А вас вообще устраивают ваши тесты и надежность покрытия вашей системы?
Собственно, что и требовалось доказать. Результаты опроса абсолютно совпадают с моей картиной мира, поэтому встречайте тред про надежность 👇 twitter.com/jsunderhood/st…

Для начала давайте разберемся, что же такое та самая надежность в контексте тестирования? Ваша система на 100% защищена тестами, если:

Вы можете внести любое изменение в бизнес логику и ваши тесты упадут. Или по-другому: изменения в бизнес-логике требуют изменения тестов.

Вы можете произвести ЛЮБОЙ рефакторинг и хотя бы не переписывать тесты заново, а в идеале вообще не менять тесты И конечно когда тесты пройдут – быть уверены что приложение действительно работает.

Вы можете обновить любую зависимость в вашей системе, даже на мажорную версию, и если тесты прошли – быть уверены что система работает.

Ваши тесты не падают на каждый чих, так что красный билд превращается в белый шум (привет jest -u на каждом коммите). На самом деле крайне важный пункт. Если фиксить тесты после любого изменения входит в привычку, то даже действительно серьезная ошибка может быть пропущена.

Возможно, звучит как что-то невозможное. Если бы можно было легко писать такие тесты, то а каких багах и тестировщиках может идти речь? Но мы хотя бы избегать того, что напрочь гробит надежность и делает тесты бесполезными:

И первое, самое очевидное – это недостаточное покрытие приложения тестами. Даже внимание уделять этому не буду. > Но хочу отметить, что нужно опираться не на CODE coverage, а на USE CASE coverage. Если захотите можем сделать отдельный тред о покрытиях.

Итак продолжаем. Поговорим за use case coverage: автоматически высчитать его невозможно, потому что 1 строкой кода может обрабатываться несколько сценариев. Это хорошо описано в статье kentcdodds.com/blog/how-to-kn…

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

Но и code coverage выбрасывать не стоит. Просто важно понимать что написать 100% coverage можно с абсолютно отвратительными и бесполезными тестами. То есть, если коверейджа мало – это однозначно плохо, но если его много – это не значит что все хорошо.

Собственно дальше о том что гробит надежность. И первое что приходит на ум — неправильное и слишком частое использование моков. Например, часто вижу проблему когда начинается замокивание частей бизнес логики внутри тестов, а интеграция модулей остается непокрытой.

В принципе использование юнит-тестов ведет к проблемам надежности. Чем выше мы взбираемся по пирамиде, тем более надежные у нас тесты. На этой неделе мы к этому еще вернемся, но в данном контексте: Чем меньше у вас юнит-тестов, тем надежнее и устойчивее к изменениям ваша система

Моки в тестах это вообще довольно опасная вещь, потому что мы сознательно вырезаем кусок кода нашего приложения в тестах.
 Это примерно тоже самое что писать any в ts. Если можно без них обойтись - лучше обойтись.

И вообще! Хватит мокать функции только потому что они находится в другом файле или потому что это внешняя зависимость. Кто вам сказал что эта зависимость или этот код будет работать правильно через 2/3/10 лет?

Вот собственно классическая проблема юнит-тестов и overmocking-а.
 С таким тестом мы не можем ни изменить название роута, ни способ которым мы делаем запрос (например на axios) – в любом случае придется переписывать тест.
notion image

Собственно, а что мы вообще проверяем в этом тесте? То, что наш код дергает fetch и возвращает промис? Зачем нам это тестировать? Что нам это дает? - нипанятна

!! Вообще эту проблему можно описать одной фразой – “Тестирование реализации”. Тесты должны проверять не то как наш код НАПИСАН, а то как наш код РАБОТАЕТ - и будет вам счастье :)

Для всех – будет отдельный тред про пирамиду тестирования. Затравочка: Кто вам сказал что e2e тесты сломать проще чем юниты? Кто вам сказал что писать e2e тесты сложнее и дольше чем юниты?

Продолжаем эппопею. Не только моки делают тесты ненадежными. Зачастую просто лень разработчиков, которым лень писать чуть-более низкоуровневые условия assert-ов. 
Это очень хорошо видно именно во фронтовых тестах.

К примеру тесты на компоненты можно сделать практически независимыми от фреймворка. Звучит странно, согласен. И самое главное – абсолютно независимыми от реализации компонента/фреймворка/ui kit-a/дизайна.

Все наши тесты состоят из 3 частей: Preparation, action & assertion. 
И если можете взять любой тест на компонент, сделать его точную копию на другом фреймворке и изменить только первую preparation часть и тесты будут работать — мое почтение!

Вот собственно пример такого теста, который абсолютно никак не дергает ни React-specific ручки, ни там названия css-классов. Этот тест придётся менять только при изменении бизнес логики или при изменении внешнего API — собственно чего мы и ожидаем от тестов.
notion image

А вот пример не очень хорошего теста: Завязываемся на названии компонента Day Почему-то дергаем именно 10 день 🤦‍♂️ Завязываемся на название Popover-a и название пропса вместо того чтобы просто проверить что 'div[role="dialog"]' отрисован в html
notion image

И последний пример: Недавно смотрел доклад про рефакторинг. Ссылки не будет, бо вы меня загрызете 🙃 И дело зашло за тесты, спикер рассказывал как он целый месяц рефакторил 1 компонент, потому что ему пришлось переписать все тесты в системе, которые были завязаны на него.

Причина банальная. Все тесты были завязаны на классах нашего компонента и в итоге при попытке заизолировать стили внутри компонента – все пошло по одному месту. И автор решил переписать тесты:
notion image

И действительно – стало лучше. Вот только никто не подумал о том, что избавляясь от одних деталей реализации (названия классов) тесты теперь знают о других (названиях компонентов и пропсов).

Автор сам говорил о том, что проект изначально был написан на jquery. Тогда никто не мог подумать о том что кому-то когда-то понадобится изолироваться от классов. И опять не подумали, что например через 5 лет веб-компоненты захватят мир и опять надо будет все переписать.
notion image

Приближаемся к выводу. Тесты, даже банальные юнит тесты имеют свои подводные камни, свои "бед" и "бест" практики. Помочь писать хорошие тесты могут утилиты для тестов с правильным и строгим API которое запрещает делать всякую грязь. Например testing-library.com

Итак вывод: Если вы уважаете себя и ваш продукт, хотите чтобы он цвел и пах (и например уволить тестировщиков), а вы не чинили тесты на каждый чих – вам заранее необходимо думать о том как обеспечить “Антихрупкость” тестам и надежное покрытие пользовательских сценариев.