Pull to refresh

Comments 22

Хорошие тесты. Напишу, как пишут многие — это для простых функций! Всегда пугало не само юнит-тестирование, а работа со сложными, разветвленными и зависимыми классами. При этом всегда приводятся простейшие примеры. Предлагаю описать некоторое сложное приложение и стратегию его тестирования с использованием DDT и TDD, с использованием mosk-объектов и всех преимуществ тестов. Хочется оценить объемы тестов и тестируемого кода.
Прошу не считать данный ответ хамством :)

За несколько последний лет я убедился, что если какой-то код (или кусок кода) заставляет думать, как же его тестировать, то он неправильно написан*. Вот прямо сейчас я сижу и переделываю чужой код, который плохо тестируется (именно по указанной Вами причине — сложные, разветвленные и зависимые классы).

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

Прошу мне поверить, что я не упертый пурист и не догматик. Для меня TDD — не догма. Я нормальный инженер, и моя главная задача, чтобы мой код хорошо работал. Просто я много-много раз убеждался, что много хороших тестов — это гарантия того, что код будет работать хорошо и сегодня, и через полгода, и когда угодно. И если его изменить/улучшить, то тесты поймают все потенциальные проблемы, значит лично мне не придется отвлекаться от хабра или чем там я еще буду заниматься, чтобы выискивать баги в старом (или, еще хуже) чужом коде. Поэтому я сам пишу, как минимум, в три раза больше строк кода в тестах, чем в собственно продукте, и от своих орлов требую того же.

К чему я снова принялся доказывать, что тесты — это хорошо? А к тому, что:
1. Тесты — это хорошо
2. Если код нетривиально тестируется, значит, это плохой код, который надо переделать. Почему? См. 1.

*) Исключение составляют куски кода, которые взаимодействуют с внешними вещами — чужой онлайновый сервис, запущенные процессы, и т.д., и т.п. В этом случае тестирование действительно нетривиально, но возможно. Для этого берется самый лучший инженер в команде и он пишет фрейворк для тестирование именно таких кусков (например, собственная имплементация Amazon EC2 API. Или библиотека для запуска и гарантированного уничтожения внешних процессов. Или еще что-нибудь такое же ненужное и громоздкое. Потому что оно нужное. Может быть, самое нужное во всем продукте.

Где-то так
Хороший ответ, только:
1. Весь код, что тебе встретился не переделать;
2. Тесты для 12000 классов лишь в одном проекте из нескольких десятков проектов как то сложно себе представить;
3. Как быть, если в коде нет расчетного кода, а есть лишь взаимодействующий с внешними системами?

Я не против тестирования, я за. Но обычно в наследство достаются огромные системы без тестов.
Для унаследованного кода — да, трудно.

Но если писать с нуля и одновременно код и тесты, то это не только возможно, но и желательно.

В моем последнем проекте лично мы написали около 200000 строк кода, покрытие тестами колебалось между 75 и 85 процентами. Не было тестов на всяки геттеры/сеттеры и прочий boilerplate код. Я поспорил с менеджером на обед, что профессиональные QA не найдут багов, и выиграл (они, конечно, нашли, но это были проблемы с юзабилити и не до конца ясными требованиями. Наш код работал.)
Также прошу не считать ответ хамством, это всего лишь мое мнение :)

Целью для этой статьи ставил обзор именно TestNG, не охватывая при этом другие фреймворки(собираюсь рассмотреть их в следующих статьях). Да, примеры тестов просты, но если бы я использовал сложные примеры, многим они были бы непонятны.

Насчет сложных тестов. Большие системы обычно делятся на модули, модули на компоненты. Между компонентами/модулями существуют взаимосвязи, чем более плохо спроектированая сложная система, тем больше этих взаимосвязей. Тесты для таких систем могут быть разными.

1. Тесты для отдельных компонентов. Все(или почти все) связи заменяются на мок-объекты, тестируется каждый предоставляемый метод(или что там еще может быть). У метода есть входные параметры и ожидаемый результат. Таким образом, эти тесты сводятся к простым, создаем набор данных (входные параметры, ожидаемый результат) и тестируем на этих данных метод.

2. Тесты для групп компонентов либо модулей в целом. Здесь используются реальные компоненты, на мок-объекты заменяются компоненты из других групп/модулей. Принцип такой же как и в предыдущем примере.

3. Тесты для всей системы в целом. Здесь уже используются только реальные компоненты, причем могут прогоняться в различных средах(различные апп-сервера, различные БД). Тот же принцип, все тесты сводятся к простым.

Конечно, архитектура приложения может быть сложной иногда даже пластилиновой, и это повлечет за собой создание такой же сложной архитектуры тестов, которая скорее всего будет находится в базовых классах в методах before и after, и будет использоваться многократно для тестирования различных компонентов. Но таккая архитектура тестов создается однажды и дальнейшее написание тестов сводится к вышеописанным действиям.
Неофитов TestNG хочу предупредить — существует определенный геморрой при запуске тестов TestNG через maven-surefire-plugin. В энторнетах примерно миллион блогов, посвященных этому вопросу, ищите и найдете, какую версию плагина использовать с какой версией TestNG. Ничего фатального, просто некоторые версии этих вещей несовместимы.
Я и мои коллеги предпочитаем использовать JUnit вместо NG именно по этой причине — плохая поддержка со стороны maven. Плюс плагин к Eclipse у JUnit лучше.
К счастью с данными проблемами не знаком, возможно потому что использую всегда последнюю версию обоих. Насчет плохой поддержки со стороны мавена не согласен, что такого особенного у поддержки JUnit-а, чего нет у TestNG?
Эээ… а почему «вместо»? У нас, например, куча тестов на JUnit3, JUnit4, TestNG, все вместе прекрасно работает. В зависимости от потребности используем наиболее подходящий инструмент.

Разобраться с очередной несовместимостью версий — час от силы, требуется это сделать один или два раза за весь проект.
Потому что если проект новый, то проще использовать одну библиотеку, а не 2-3.
А какими IDE и какими плагинами вы пользуетесь для запуска этого хозяйства?
Я пользуюсь идеей, поддержка TestNG отличная, иногда запускаю в ней тесты через мавен, есть возможность дебажить запущеные так тесты.

Плагины для мавена:
maven-surefire-plugin — без комментариев
maven-failsafe-plugin — интеграционные тесты, запускаются после сборки модуля.
maven-invoker-plugin — для запуска отдельного проекта с тестами(запускаемыми первыми двумя плагинами), полезно для тестов в различной среде исполнения, с различными зависимотсями и т.д.
Ок, спасибо.
Неочевиден invoker — его я не использовал еще, и на первый взгляд он выглядит удобно, когда интеграционные тесты лежат в отдельном проекте/модуле (особенно когда их пишет отдельная от разработчиков группа QA).

Вообще я имел в виду именно плагины к IDE, которые могут красиво графически показать зелененькие и красненькие полоски одним тычком мыши, без необходимости создания Run Configuration в Eclipse, например. Для проекта или отдельного класса. С мавеном-то проблем обычно не возникает. Я использовал плагин для TestNG (так и называется — «TestNG plug-in for Eclipse»). Так и не научил его нормально работать без xml-конфига тестовых сьютов, остановился на ран конфигах для mvn test. Плагин для JUnit, судя по тому что я о нем помню, таких проблем не имеет — и для проекта, и для класса все запускается одним тычком.
В идее все проще, контекстное меню -> запустить или продебажить тест-метод(тест-класс) -> создается временный xml-ничек во временной папочке и тесты пошли.
Честно говоря, я не очень понимаю, что означает «нормально работать без xml-конфига». Он что, не работал просто через Alt-Shift-X, N? Или «Run As..» -> «TestNG»? А как у вас тестовый класс назывался?
Оно, конечно, правильно, но вы упускаете существование кучи библиотек дл я тестирования, имеющий в своей основе JUnit 3, 4, или TestNG.

IDE — Эклипс, плагин для TestNG, и все прекрасно запускается через Alt-Shift-X, N (TestNG) или Alt-Shit-X, T (JUnit любой версии).

В мавене используется maven-surefire-plugin, в котором тоже все просто работает (если соблюдены конвенции названия тестов). Пару раз бывало, что какая-то версия surefire не работала с какой-то конкретной версией TestNG, но это все решаемо.
А как в TestNG делается то, что в JUnit 4 реализуется с помощью @org.junit.runner.RunWith?
Экую картинку вы выбрали для фреймворка тестирования :) Не очень стимулирует :).
Тесты это хорошо. Полностью согласен с вами.
Тесты сложного функционала это очень хорошо, но на их реализацию уходит нередко чуть ли не в три раза больше времени, чем на реализацию непосредственно самого кода. Посему нередко эти куски и обходят стороной и проверяют работоспособность на рабочем проекте уже.
Второй десяток третьего тысячелетия. Тесты стали настолько сложными, а их конфигурация настолько нетривиальная, что пришлось писать тесты для тестов, используя более старые, но более простые фреймворки )))
«в каждой шутке есть только доля шутки» :)

Раз уж вы решили использовать тесты (кроме простых а-ля проверяем, правильно ли выполняется валидация e-mail адреса), то относитесь к ним как к основному коду: если проект довольно большой и предполагает долгую жизнь, в течении которой требования будут меняться (а они практически всегда меняются), то тесты нужно проектировать, а не лепить как попало (продумывать иерархию классов, архитектуру и всё остальное). Тогда и на изменения будет легче реагировать.

Тут ведь главное что: поменялись требования — правим код и тесты. Если просто рефакторинг или багфиксинг (в 99%) — тесты не трогаем.
Кстати, было бы интересно собрать статистику — какую из двух библиотек (JUnit или TestNG) люди предпочли бы при разработке нового проекта с нуля и, соответственно, почему.
Sign up to leave a comment.

Articles