1. Введение
Тестовые классы часто содержат переменные-члены, относящиеся к тестируемой системе, макетам или ресурсам данных, используемым в тесте. По умолчанию и JUnit 4, и 5 создают новый экземпляр тестового класса перед запуском каждого тестового метода. Это обеспечивает четкое разделение состояний между тестами.
В этом руководстве мы узнаем, как JUnit 5 позволяет нам изменять жизненный цикл тестового класса с помощью аннотации @TestInstance
. Мы также увидим, как это может помочь нам в управлении большими ресурсами или более сложными отношениями между тестами.
2. Жизненный цикл тестирования по умолчанию
Давайте начнем с рассмотрения жизненного цикла тестового класса по умолчанию, общего для JUnit 4 и 5:
class AdditionTest {
private int sum = 1;
@Test
void addingTwoReturnsThree() {
sum += 2;
assertEquals(3, sum);
}
@Test
void addingThreeReturnsFour() {
sum += 3;
assertEquals(4, sum);
}
}
Этот код вполне может быть тестовым кодом JUnit 4 или 5, за исключением отсутствующего ключевого слова public
, которое не требуется для JUnit 5.
Эти тесты проходят успешно, потому что перед вызовом каждого тестового метода создается новый экземпляр AdditionTest
. Это означает, что значение переменной sum
всегда устанавливается в 1
перед выполнением каждого теста.
Если бы был только один общий экземпляр тестового объекта, переменная sum
сохраняла бы свое состояние после каждого теста. В результате второй тест провалился.
3. Аннотации @BeforeClass
и @BeforeAll
Бывают случаи, когда нам нужно, чтобы объект существовал в нескольких тестах. Давайте представим, что мы хотели бы прочитать большой файл, чтобы использовать его в качестве тестовых данных. Поскольку повторение этого перед каждым тестом может занять много времени, мы можем предпочесть прочитать его один раз и сохранить для всего тестового набора.
JUnit 4 решает эту проблему с помощью аннотации @BeforeClass
:
private static String largeContent;
@BeforeClass
public static void setUpFixture() {
// read the file and store in 'largeContent'
}
Следует отметить, что мы должны сделать переменные и методы, аннотированные @BeforeClass
JUnit 4, статическими.
JUnit 5 предлагает другой подход. Он предоставляет аннотацию @BeforeAll
, которая используется в статической функции для работы со статическими членами класса.
Однако @BeforeAll
также можно использовать с функцией экземпляра и членами экземпляра, если жизненный цикл тестового экземпляра изменен на per-class
.
4. Аннотация @TestInstance
Аннотация @TestInstance
позволяет настроить жизненный цикл тестов JUnit 5.
@TestInstance
имеет два режима. Одним из них является LifeCycle.PER_METHOD
(по умолчанию). Другой — LifeCycle.PER_CLASS
. Последнее позволяет нам попросить JUnit создать только один экземпляр тестового класса и повторно использовать его между тестами.
Давайте аннотируем наш тестовый класс аннотацией @TestInstance
и используем режим LifeCycle.PER_CLASS
:
@TestInstance(LifeCycle.PER_CLASS)
class TweetSerializerUnitTest {
private String largeContent;
@BeforeAll
void setUpFixture() {
// read the file
}
}
Как мы видим, ни одна из переменных или функций не является статической. Нам разрешено использовать метод экземпляра для @BeforeAll
, когда мы используем жизненный цикл PER_CLASS
.
Также следует отметить, что изменения, внесенные в состояние переменных экземпляра одним тестом, теперь будут видны другим.
5. Использование @TestInstance(PER_CLASS)
5.1. Дорогие ресурсы
Эта аннотация полезна, когда создание экземпляра класса перед каждым тестом довольно дорого. Примером может быть установление соединения с базой данных или загрузка большого файла.
Решение этой проблемы ранее приводило к сложному сочетанию статических переменных и переменных экземпляра, что теперь проще с общим экземпляром тестового класса.
5.2. Преднамеренное совместное использование состояния
Совместное использование состояния обычно является антишаблоном в модульных тестах, но может быть полезным в интеграционных тестах. Жизненный цикл для каждого класса поддерживает последовательные тесты, которые преднамеренно совместно используют состояние. Это может быть необходимо, чтобы в последующих тестах не приходилось повторять шаги предыдущих тестов, особенно если приведение тестируемой системы в нужное состояние происходит медленно.
При совместном использовании состояния для последовательного выполнения всех тестов JUnit 5 предоставляет нам аннотацию @TestMethodOrder
на уровне типа. Затем мы можем использовать аннотацию @Order
для тестовых методов, чтобы выполнять их в выбранном нами порядке.
@TestMethodOrder(OrderAnnotation.class)
class OrderUnitTest {
@Test
@Order(1)
void firstTest() {
// ...
}
@Test
@Order(2)
void secondTest() {
// ...
}
}
5.3. Совместное использование некоторого состояния
Проблема с совместным использованием одного и того же экземпляра тестового класса заключается в том, что некоторые члены могут нуждаться в очистке между тестами, а некоторые — поддерживаться на протяжении всего теста.
Мы можем сбросить переменные, которые необходимо очищать между тестами, с помощью методов, аннотированных с помощью @BeforeEach
или @AfterEach
.
6. Заключение
В этом руководстве мы узнали об аннотации @TestInstance
и о том, как ее можно использовать для настройки жизненного цикла тестов JUnit 5.
Мы также рассмотрели, почему может быть полезно совместное использование одного экземпляра тестового класса с точки зрения обработки общих ресурсов или преднамеренного написания последовательных тестов.
Как всегда, код для этого туториала можно найти на GitHub .