JUnit, часть 3: модели кастомизации
Изменение архитектуры - не всё, чем JUnit 5 отличается от предыдущей версии. Второе отличие касается модели кастомизации.
В этом посте поговорим, зачем это нужно в тестовом фреймворке, и про разницу между 4 и 5 версией.
Чтобы было понятнее, давайте опишем простую задачу и будем её понемногу усложнять.
Допустим, нужно измерить время выполнения каждого теста: запустить таймер вначале и вывести время выполнения в конце.
Для одного класса это несложно - просто добавляем методы с аннотациями
А как посчитать время для всех классов? Здесь варианта два:
🔸 Вынести общий код в отдельный класс, в каждый класс-тест добавить методы Before и After. Решение рабочее, но придётся копипастить методы в каждый класс.
🔸 Внедрить логику где-то на верхнем уровне и включать/выключать её через настройки или аннотации.
Это и есть кастомизация - предусмотренные библиотекой места "встраивания" новой логики. JUnit 4 и 5 используют для этого разные механизмы. Давайте кратко их обсудим.
JUnit 4 Runner
Переопределяем жизненный цикл теста целиком. Наследуемся от интерфейса
Примеры:
▫️
▫️
▫️
▫️
Главный минус - жизненный цикл только один, значит
JUnit 4 Rule
Переопределяем интерфейс
▪️
▪️
Плюсы-минусы:
✅ Можно использовать несколько rule в одном классе
❌ Работает в рамках одного метода и по сути похож на before/after.
JUnit 5 Extension
Жизненный цикл теста разбивается на 10+ фаз. К каждой из них можно присоединиться, если переопределить нужный интерфейс:
Реализуем нужные интерфейсы, регистрируем класс и готово. Похожий механизм используется в Spring.
✅ Класс может использовать несколько экстеншенов
✅ Можно вклиниться на любых этапах жизненного цикла
✅ В интерфейсах доступен контекст выполнения и вся информация про тесты, в итоге возможностей гораздо больше
В JUnit 5 полностью убрали поддержку
______
Разбирать чужие кейсы полезно, но не всегда увлекательно. Поэтому вот интересный факт про разработку JUnit.
JUnit - опенсорсный проект, где никто никому не платил за работу.
Но рефакторинг назревал много лет. Однажды ребята решили, что такие грандиозные планы требуют фулл тайм и объявили краудфандинг на JUnit 5.
Сумма требовалась небольшая - 25 тысяч евро, меньше двух миллионов рублей. В итоге собрали в 2 раза больше, и уже через 6 недель был готов первый прототип.
Меня это очень впечатляет, особенно в сравнении со стоимостью и скоростью разработки в энтерпрайзе🙈
Изменение архитектуры - не всё, чем 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В JUnit 4 есть несколько готовых правил:
public Timeout globalTimeout = Timeout.seconds(10);
▪️
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 недель был готов первый прототип.
Меня это очень впечатляет, особенно в сравнении со стоимостью и скоростью разработки в энтерпрайзе🙈