Аннотации в JUnit и TestNG используются для указания жизненного цикла тестов и для выполнения определённых действий до или после тестов. Эти аннотации позволяют автоматизировать подготовку и очистку тестовых данных, а также управлять порядком выполнения тестов и наборов тестов.
Основные аннотации JUnit:
1. @Test
- Означает, что метод является тестовым. JUnit будет его выполнять при запуске тестов.
- Пример:
@Test
public void testMethod() {
// Test code here
}
2. @Before
- Выполняется перед каждым тестовым методом. Используется для настройки окружения.
- Пример:
@Before
public void setUp() {
// Initialization code here
}
3. @After
- Выполняется после каждого тестового метода. Используется для освобождения ресурсов.
- Пример:
@After
public void tearDown() {
// Cleanup code here
}
4. @BeforeClass
- Выполняется один раз перед всеми тестами в классе. Метод, помеченный этой аннотацией, должен быть статическим (static).
- Пример:
@BeforeClass
public static void setUpBeforeClass() {
// Code to run once before all tests
}
5. @AfterClass
- Выполняется один раз после всех тестов в классе. Метод также должен быть статическим (static).
- Пример:
@AfterClass
public static void tearDownAfterClass() {
// Code to run once after all tests
}
6. @Ignore
- Пропускает тест. Этот тест не будет выполняться.
- Пример:
@Ignore("Not implemented yet")
@Test
public void testNotImplemented() {
// Test code (not executed)
}
7. @ParameterizedTest
- В JUnit 5 появилась поддержка параметризованных тестов, которые позволяют запускать тесты с различными наборами данных.
- Пример:
@ParameterizedTest
@ValueSource(ints = {1, 2, 3})
public void testWithParameters(int number) {
assertTrue(number > 0);
}
Основные аннотации TestNG:
1. @Test
- Так же, как и в JUnit, указывает, что метод является тестом.
- Пример:
@Test
public void testMethod() {
// Test code here
}
2. @BeforeMethod
- Выполняется перед каждым тестовым методом. Аналогично аннотации @Before в JUnit.
- Пример:
@BeforeMethod
public void setUp() {
// Initialization code here
}
3. @AfterMethod
- Выполняется после каждого тестового метода. Аналогично аннотации @After в JUnit.
- Пример:
@AfterMethod
public void tearDown() {
// Cleanup code here
}
4. @BeforeClass
- Выполняется один раз перед тестами в классе. Аналогично JUnit.
- Пример:
@BeforeClass
public void setUpBeforeClass() {
// Code to run once before all tests
}
5. @AfterClass
- Выполняется один раз после всех тестов в классе. Аналогично JUnit.
- Пример:
@AfterClass
public void tearDownAfterClass() {
// Code to run once after all tests
}
6. @BeforeSuite
- Выполняется один раз перед запуском тестового набора (suite). Используется для глобальной начальной настройки.
- Пример:
@BeforeSuite
public void beforeSuite() {
// Code to run before the suite
}
7. @AfterSuite
- Выполняется один раз после выполнения всех тестов в тестовом наборе.
- Пример:
@AfterSuite
public void afterSuite() {
// Code to run after the suite
}
8. @BeforeTest / @AfterTest
- Эти аннотации используются для выполнения кода до и после каждого теста в тестовом наборе (suite).
- Пример:
@BeforeTest
public void beforeTest() {
// Code to run before each test in suite
}
@AfterTest
public void afterTest() {
// Code to run after each test in suite
}
9. @DataProvider
- Используется для создания параметризованных тестов. Позволяет передавать тестам наборы данных.
- Пример:
@DataProvider(name = "data-provider")
public Object[][] dpMethod() {
return new Object[][] { {1}, {2}, {3} };
}
@Test(dataProvider = "data-provider")
public void testWithDataProvider(int number) {
assertTrue(number > 0);
}
Аннотации в JUnit и TestNG позволяют легко управлять жизненным циклом тестов и их конфигурацией. Они позволяют выполнять подготовительные действия перед тестами, очищать ресурсы после тестов, а также управлять порядком и параметризацией тестов.
Чтобы лучше "подружиться" с аннотациями и понимать почему они работают или не работают в какой-то ситуации, давайте разберем как они устроены?
Что скрывается под капотом аннотаций JUnit и TestNG?
Чтобы понять, как они работают "под капотом", нужно рассмотреть несколько ключевых аспектов: как обрабатываются аннотации в Java, как фреймворки JUnit и TestNG используют рефлексию для выполнения тестов, и как организован жизненный цикл тестов.
Аннотации в Java
Аннотации в Java — это метаданные, которые можно добавить к классам, методам, полям или конструкторам. Они никак не влияют на поведение кода во время компиляции и выполнения (если только не используются соответствующие механизмы), но могут быть использованы для передачи дополнительной информации о коде.
Чтобы аннотации могли быть доступны во время выполнения программы, они должны быть помечены как аннотации с политикой ретенции (retention policy) RUNTIME. Это означает, что аннотации будут сохранены в байт-коде и доступны через рефлексию во время выполнения.
Пример аннотации с политикой RUNTIME:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Test {
}
Рефлексия в Java
Рефлексия — это механизм, который позволяет программам исследовать и модифицировать свою структуру во время выполнения. В контексте тестовых фреймворков, рефлексия используется для поиска методов, помеченных аннотациями, и их последующего вызова.
Основные операции рефлексии:
1. Получение методов класса:
Фреймворк использует методы класса Class для получения всех методов класса:
Method[] methods = MyClass.class.getDeclaredMethods();
2. Поиск аннотаций:
Для того чтобы найти методы, аннотированные, например, @Test, используется метод isAnnotationPresent:
for (Method method : methods) {
if (method.isAnnotationPresent(Test.class)) {
// Это тестовый метод
}
}
3. Вызов методов через рефлексию:
После того как фреймворк находит тестовые методы, он может вызвать их с помощью метода invoke:
method.invoke(testInstance);
Таким образом, аннотации сами по себе не выполняют никакой работы. Основная магия заключается в том, как фреймворк использует рефлексию для поиска и вызова методов, помеченных этими аннотациями.
Как работает JUnit под капотом?
На самом деле TestNG работает аналогично JUnit, но имеет более сложную архитектуру для поддержки дополнительных возможностей. Если вам будет интересно как работает под капотом TestNG пишите в комментариях и мы это обязательно рассмотрим.
1. Загрузка и анализ тест-классов
Когда JUnit запускается, он начинает с поиска и анализа всех классов, которые должны быть протестированы. Это может быть указано явно или через конфигурацию.
Для каждого тестового класса JUnit ищет методы, аннотированные с помощью следующих аннотаций:
- @Test — методы тестирования.
- @Before, @BeforeEach — методы, которые подготавливают окружение перед тестом.
- @After, @AfterEach — методы, которые очищают состояние после теста.
- @BeforeClass, @BeforeAll — методы, которые выполняются один раз перед всеми тестами.
- @AfterClass, @AfterAll — методы, которые выполняются один раз после всех тестов.
2. Выполнение тестов
После того как JUnit идентифицирует все тестовые методы и методы подготовки/очистки, он представляет это в виде объекта TestClass, содержащего информацию о всех этих методах.
Когда JUnit выполняет тесты:
1. Создание экземпляра тестового класса:
Для каждого теста создаётся новый экземпляр тестового класса (если это не статический метод).
Object testInstance = testClass.getDeclaredConstructor().newInstance();
2. Запуск методов подготовки:
JUnit вызывает все методы, помеченные аннотацией @Before или @BeforeEach (в зависимости от версии JUnit). Это делается через рефлексию:
for (Method beforeMethod : beforeMethods) {
beforeMethod.invoke(testInstance);
}
3. Запуск теста:
После выполнения методов подготовки JUnit вызывает сам тестовый метод:
testMethod.invoke(testInstance);
4. Запуск методов очистки:
После выполнения теста JUnit вызывает все методы, помеченные аннотацией @After или @AfterEach:
for (Method afterMethod : afterMethods) {
afterMethod.invoke(testInstance);
}
5. Обработка исключений и результатов:
Если тест выбрасывает исключение, оно будет перехвачено и зарегистрировано как ошибка. В противном случае тест считается успешным.
3. Выполнение методов с аннотациями @BeforeClass и @AfterClass
Эти методы выполняются один раз до и после всех тестов в классе. Поскольку они статические, экземпляр класса не создаётся.
JUnit вызывает такие методы только один раз на уровне класса.
Заключение
Под капотом аннотации JUnit и TestNG работают с помощью механизма рефлексии, который позволяет фреймворку:
1. Искать методы, аннотированные специфическими аннотациями (@Test, @Before, @After и т.д.).
2. Создавать экземпляры тестовых классов для каждого теста (или использовать статические методы для класса).
3. Вызывать методы тестов и методы подготовки/очистки через рефлексию.
4. Обрабатывать результаты тестов и исключения.
Хотя аннотации сами по себе не выполняют никакой работы, рефлексия и инфраструктура фреймворков позволяют интерпретировать их и управлять выполнением тестов в соответствии с определёнными правилами.
Если Вам интересно, что еще можно найти на канале QA Helper, прочитайте статью: Вместо оглавления. Что вы найдете на канале QA Helper - справочник тестировщика?
Не забудьте подписаться на канал, чтобы не пропустить полезную информацию: QA Helper - справочник тестировщика
Пишите в комментариях какой пункт было бы интересно рассмотреть более подробно.
Обязательно прочитайте: Что должен знать и уметь тестировщик
Также будет интересно почитать: Вопросы которые задают на собеседовании тестировщикам