Три смертных греха автотестов
В последние годы из всех утюгов только и говорят, что без автотестов никуда. Любой уважающий себя разработчик должен писать тесты. Любой стремящийся не оказаться на обочине жизни тестировщик должен писать автотесты. Матерого автотестера от новичка отличает понимание, что не все тесты одинаково полезны. И иногда лучше не писать ничего. Как учила нас мама — «не пиши, за умного сойдешь». Моя статья будет не о том, как писать тесты, а о том, как их писать не надо.
Дисклеймер: в статье будут примеры кода на C#, но я постаралась сделать их максимально понятными для людей, знакомых с любым языком программирования. Писать тесты будем на функцию кормления кота:
Проверь то, не знаю что
Хорошо ли вы понимаете, что проверяют ваши тесты? Пфф, что за вопрос, ведь вы писали их на конкретный сценарий. На практике часто оказывается так, что тесты проверяют не то, что вы задумывали. Падают не в тех местах, где должны. Проходят, когда в коде есть ошибка. Например, вы решили написать позитивный тест на нашу функцию, сценарий «Если передать валидное значение количества порций, кот будет покормлен». Тест может выглядеть как-то так:
Хороший ли это тест? А давайте проверим. Изменим в нашей функции условие на минимальное количество порций:
Серьёзное изменение, правда? А наш тест как был зеленым, так и остался. И если изменение было случайным, этот баг мы не поймаем.
Вывод номер раз: проверяйте, что ваш тест падает в ситуациях, когда должен упасть. Делать это для всех тестов, конечно, накладно, но я рекомендую это упражнение начинающим тестописателям, а также при написании сложных интеграционных тестов.
Вывод номер два: не забывайте про технику граничных значений.
Секунды имеют значение
Как определить, быстро ли проходят ваши тесты? Установить разумные для вашей системы и команды значения. Как определить, могут ли ваши тесты проходить быстрее? Экспериментировать. Часто тесты искусственно замедлены по одной из следующих причин:
содержат лишние действия
не используют моки там, где это оправдано
без необходимости обращаются к файловой системе
Покажу на примере второй причины. Мы хотим проверить, что наша система не даст покормить кода, если CatProvider говорит, что кот — не наш. Подчеркну, это не интеграционный тест на проверку связи с CatProvider, а юнит на работу нашей системы. Вот что получилось:
По коду тестируемого метода видно, что в тесте произойдет реальное обращение к CatProvider. Если это сторонний нагруженный сервис, тест может ждать несколько секунд, прежде чем получит ответ. А еще сервис может быть остановлен в момент прогона наших тестов, и тест упадет. В этом юнит тесте уместно добавить обращение к заглушке вместо реального сервиса. Выглядеть это будет так:
Чтобы магия случилась, нужно изменить продуктовый код, тем или иным способом добавив работу с интерфейсом ICatProvider и определение, какую из реализаций использовать. Это нормальная ситуация — иногда приходится вносить в продуктовый код изменения для улучшения тестируемости, не влияя на основную функциональность.
Помните, что одна из основных целей автотестов — предоставлять быстрый фидбек о влиянии изменений. Поэтому не выпускайте из-под контроля скорость прогона ваших тестов.
Скажи зависимости нет
Чаще всего для тестов (особенно сквозных end-to-end) вам нужно много мноооого данных. Некоторые команды приходят к решению использовать уже готовые — созданные руками на тестовой среде или скопированные с прода. В чем опасность? В том, что эти данные в любой момент могут поменяться, ваши тесты их не контролируют. Я много раз сталкивалась с нестабильностями в тестах именно по этой причине. Посмотрим, как это выглядит:
Что с этим делать? Создавать данные прямо в тестах. Не забывать чистить данные в конце. Не забывать про независимость тестов.
Стоит отметить, что готовые данные часто используют ради скорости, т.к. на создание данных нужно потратить N секунд, и это как будто бы противоречит предыдущему пункту. Но а) стабильность важнее скорости, б) создание данных не обязано быть долгим процессом. К примеру, наши разработчики смогли научить приложение запускаться в InMemory режиме, что позволяет быстро создавать данные на чистой базе и перестать тратить время на анализ ложно негативных тестов.
Вместо послесловия
Всё вышесказанное было выстрадано годами работы, но не является руководством к действию. Автор признает, что в вашей ситуации описанные изменения могут привести к ухудшению тестовой системы. Думайте своей головой, но не забывайте сверяться с реальностью. Всем бобра и зеленых автотестов!