“Хорошими манерами обладает тот,
кто наименьшее количество людей
ставит в неловкое положение.”
Дж. Свифт
Привет, коллеги! Сегодня я бы хотел поговорить о Unit-тестировании и некоторых “правилах” при их написании. Конечно, они неформальные и не обязательны к выполнению, но при их соблюдении всем будет приятно и легко читать и поддерживать тесты, которые вы написали. Мы в Wrike видели достаточно Unit-тестов, чтобы понять основные проблемы, которые возникают при их написании и поддержке, и сформулировать несколько правил для их предотвращения.
1. Unit-тесты нужно писать. Да, как бы банально это не звучало, но писать их нужно. Каждый кусок логики приложения должен быть протестирован, чтобы в будущем избежать проблем. А они могут возникнуть при изменении логики, рефакторинге, или даже при обновлении версии зависимых библиотек. И чем больше покрытие кода тестами, тем быстрее проблема будет обнаружена и исправлена.
2. Это правило очень актуально для тех, кого заставляют покрывать код тестами, и оно звучит так: Тесты — это тоже код, и относиться к нему нужно как к рабочему коду. Это касается и нейминга переменных, и форматирования кода внутри теста, и, особенно, названий тестовых методов. Конечно, написание адекватного имени переменной занимает немного больше времени и ударов по клавиатуре, чем “int i = 0;”, но это повышает читабельность тестов и легкость их поддержки.
Угадайте, что проверяет упавший тестовый метод?)
3. Третье правило
настоящего джентльмена — следи за путями. Конечно, всегда неприятно увидеть при запуске тестов, вот такую ошибку:
И даже не потому, что тебя зовут не Andrey, а потому что у тебя мак. И как же быть в такой ситуации, спросите вы? Ответ прост — Относительные пути. Вот пример —
Это и гораздо лаконичнее, и меньше шансов получить непредвиденную ошибку.
4. Чаще используйте заглушки (моки) вместо реальных объектов. Моки — это здорово! Ими можно управлять так, как нужно в конкретном тесте. Но, конечно, не стоит забывать сбрасывать состояние заглушек перед каждым тестовым методом. Использование заглушек повышает автономность теста и его гибкость. Не нужно подгонять состояние системы для конкретного случая, а просто настроил заглушку на возвращение нужного значения при вызове определенного метода и все. Хочется проверить другую ситуацию — исправил возвращаемое значение на другое. Легко и просто. И самое главное, что состояние всей системы при этом не изменяется — она ничего не записывает на диск, не передает по сети, не пересчитывает массивы данных, не лезет в другие сервисы. Просто заглушка и возвращаемое значение.
Для использования заглушек в тестах я использую фреймворк Mockito. С его помощью создавать заглушки очень просто. Вот например:
Здесь создается мок объекта calendar и передается в объект calendarService. Далее моки инициализируются в методе setUp. Затем непосредственно внутри теста мок настраивается и тест проверяет isModern, если тип календаря разный или не задан вовсе. При этом не пришлось пересоздавать CalendarService, а создание моков и генерация возвращаемых значений заняло всего несколько строк.
5. Пишите осмысленные сообщения на случай падения теста. Самое часто встречающееся сообщение, которое я видел, разбирая упавшие тесты на TeamCity — это
Ну сразу же все понятно! Но бывает, что сообщение об ошибки все-таки есть, но пользы от него…
А вот уже хорошее, но еще не идеальное, сообщение, в котором сразу описано, что проверялось
Идеальным можно считать сообщение, которое не только показывает что мы проверяем, но и почему мы это ожидаем
Но здесь показана достаточно простая проверка, а если нужно сравнить пару массивов с данными в разном порядке? Какое сообщение нужно написать здесь? Для этого я советую воспользоваться фреймворком AssertJ. По ссылке много простых и понятных примеров использования, после которых вам захочется воспользоваться этим фреймворком! AssertJ позволяет меньше задумываться о написании сообщения в случае ошибки, а также проверить кучу всего одной строкой, экономя место. Например проверка:
Выдаст нам замечательное сообщение об ошибке:
И все понятно — что случилось и по какой причине! Можно идти и исправлять ошибки.
И еще раз — Адекватные сообщения в ошибках тестов экономят время и нервы тех, кто будет эти тесты разбирать. Возможно, это будете вы сами через год.
6. Убирайте за собой мусор (нет, это не про запуск GarbageCollector-a). Создали файл, сделали запись в базу или дернули ручку создания пользователя? Не поленитесь и почистите за собой после теста. Файлы копятся, база обрастает кучей мусора и в системе появляются толпы фейковых пользователей. Старайтесь сохраняйте в чистоте не только своё рабочее место, но и рабочее окружение.
7. Проверьте, что тест запускается где-то еще, помимо вашей локальной машины. Если у вас есть сервер CI или какое-то другое место, где вы прогоняете тесты, проверьте, что тест запустился и там. Например, тесты на сервере CI запускаются из определенного пакета, а вы положили свой в другой пакет. Или тесты запускаются по определенному имени, например *UTest, а вы назвали свой класс TestUid. Или тесты запускаются по группам, а вы забыли проставить определенную группу для своего теста. Или… Можно придумать много случаев, когда свеженаписанный тест так ниразу и не запустится где-то кроме вашей локальной машины. И тогда пользы от него не так уж и много!
В результате получился небольшой список правил хорошего тона при написании Unit-тестов. Надеюсь эта статья поможет ещё немного поднять культуру тестов и тестирования в вашей команде. Если вам есть чем дополнить мой список, добро пожаловать в комментарии.
Автор: Andrey