Параметризованные тесты

При написании unit тестов часто возникает необходимость проводить абсолютно идентичные испытания кода с разными данными. Примером такого кода может быть вычисление квадратного корня — для входных данных выше нуля мы ожидаем получить значения корня, а при передаче числа меньше 0, код должен возвращать ошибку (или, в редакции для эстетов, комплексное число). Очевидно, что для проверки этого поведения придётся написать два абсолютно идентичных теста, отличающихся только данными. Это настолько некрасиво, что хочется сразу с этим что-нибудь сделать.

В JUnit есть механизм, позволяющий отделить код теста от данных теста — параметризованные тесты.

Подготовка

Создадим пустой maven проект и добавим в него 4:

Класс для теста

Тестировать мы будем функцию по вычислению длины ортодромии. Вычисление расстояний между точками интересно тем, что имеет кучу особых условий — расстояние всегда должно быть кратчайшим, расстояние между двумя точками с одними координатами будет нулём, плюс должны корректно вычисляться расстояния между точками в разных полушариях: северном/южном, западном/восточном. Сама функция крайне проста:

Параметризованный тест

Параметризованный тест — хороший пример использования Runners. Аннотация  @RunWith(Parameterized.class) говорит нам, что тест будет исполняться с runner «Parameterized», который и реализует параметричекое тестирование и разрешает использовать конструкторы в классах тестов.

Сами параметры определяются в методе с аннотацией @Parameters:

Метод возвращает Iterable, каждая запись которого представляет массив объектов. Одна запись — один набор данных для теста. В моём примере это таблица координат и расстояний, но это могут быть и любые, сколь угодно сложные, объекты. Аннотации @Parameters можно передать имя теста, для каждой итерации.  В имени теста можно ссылаться на параметры по их индексам. Если имена заданы, то когда какой-либо тест проваливается, Вы сможете определить, с каким именно набором данных возникла проблема:

Параметры передаются в конструктор, причём позиционно — нулевой элемент набора данных станет первым параметром конструктора итд. JUnit сам проверит количество и типы параметров и выкинет исключение, если они не будут cовпадать. Параметры для теста передаются один раз на итерацию, а не перед вызовом каждого теста, о чём нельзя забывать. Проще говоря — если у вас в классе несолько тестов, то каждый набор параметров будет установлен один раз, перед запуском всех тестов в классе, а не каждого текста и, таким образом, тестам не стоит изменять эти данные.

Сам же тест у нас будет очень просто, про него и сказать нечего:

Исходный код примера доступен на github