2021-11-05 09:00:03
JUnit, часть 3: модели кастомизации
Изменение архитектуры - не всё, чем JUnit 5 отличается от предыдущей версии. Второе отличие касается модели кастомизации.
В этом посте поговорим, зачем это нужно в тестовом фреймворке, и про разницу между 4 и 5 версией.
Чтобы было понятнее, давайте опишем простую задачу и будем её понемногу усложнять.
Допустим, нужно измерить время выполнения каждого теста: запустить таймер вначале и вывести время выполнения в конце.
Для одного класса это несложно - просто добавляем методы с аннотациями @Before и @After.
А как посчитать время для всех классов? Здесь варианта два:
Вынести общий код в отдельный класс, в каждый класс-тест добавить методы
Before и
After. Решение рабочее, но придётся копипастить методы в каждый класс.
Внедрить логику где-то на верхнем уровне и включать/выключать её через настройки или аннотации.
Это и есть кастомизация - предусмотренные библиотекой места "встраивания" новой логики. JUnit 4 и 5 используют для этого разные механизмы. Давайте кратко их обсудим.
JUnit 4 Runner
Переопределяем жизненный цикл теста целиком. Наследуемся от интерфейса Runner или абстрактного класса, в нужных местах добавляем нужные действия. Теперь тесты запускаются не по стандартной схеме, а по той, что прописана в новом классе.
Примеры:
@RunWith(Parameterized.class) запускает параметризованные тесты
@RunWith(Suite.class) запускает наборы тестов
@RunWith(SpringJUnit4ClassRunner.class) добавляет спринговые активности до и после запуска теста
@RunWith(MockitoJUnitRunner.class) позволяет использовать заглушки
Главный минус - жизненный цикл только один, значит Runner для теста может быть только один. Не получится совместить несколько фич, например, параметризованные тесты с заглушками.
JUnit 4 Rule
Переопределяем интерфейс TestRule и задаём действие до и после выполнения теста. В тестах выглядит как просто поле:
@Rule
public Timeout globalTimeout = Timeout.seconds(10);
В JUnit 4 есть несколько готовых правил:
TemporaryFolder - создать временную папку для теста
ExternalResource - открыть и закрыть внешний ресурс(файл, сокет, БД)
Плюсы-минусы:
Можно использовать несколько rule в одном классе
Работает в рамках одного метода и по сути похож на before/after.
JUnit 5 Extension
Жизненный цикл теста разбивается на 10+ фаз. К каждой из них можно присоединиться, если переопределить нужный интерфейс:
BeforeAllCallback - действие перед всеми тестами
ParameterResolver - передача параметров в тест
Реализуем нужные интерфейсы, регистрируем класс и готово. Похожий механизм используется в Spring.
Класс может использовать несколько экстеншенов
Можно вклиниться на любых этапах жизненного цикла
В интерфейсах доступен контекст выполнения и вся информация про тесты, в итоге возможностей гораздо больше
В JUnit 5 полностью убрали поддержку Runner и Rule, всё переписано на
Extension API. Кодовые базы стали несовместимы между собой, поэтому и нужна библиотека
Vintage с адаптерами.
______
Разбирать чужие кейсы полезно, но не всегда увлекательно. Поэтому вот интересный факт про разработку JUnit.
JUnit - опенсорсный проект, где никто никому не платил за работу.
Но рефакторинг назревал много лет. Однажды ребята решили, что такие грандиозные планы требуют фулл тайм и объявили краудфандинг на JUnit 5.
Сумма требовалась небольшая - 25 тысяч евро, меньше двух миллионов рублей. В итоге собрали в 2 раза больше, и уже через 6 недель был готов первый прототип.
Меня это очень впечатляет, особенно в сравнении со стоимостью и скоростью разработки в энтерпрайзе
1.1K views06:00