Если вы новичок в JUnit или просто хотите освежить свои знания, этот пост расскажет вам об основах JUnit в простой и понятной форме. Независимо от того, работаете ли вы с JUnit 4 или JUnit 5, это руководство поможет вам.
Что такое JUnit
Популярный фреймворк тестирования для Java. Он помогает разработчикам писать и выполнять тесты, чтобы убедиться, что их код работает так, как ожидается. Тесты пишутся в отдельном пакете «test» в вашем проекте, а JUnit предоставляет аннотации, чтобы облегчить тестирование.
Основные аннотации в JUnit
1. @Test
Самая важная аннотация. Она сообщает JUnit, что данный метод является тестовым.
@Test public void testMethod() { // Your test logic here }
2. @BeforeClass
Этот метод запускается один раз перед всеми тест-кейсами. Он полезен для задач настройки, таких как инициализация ресурсов.
@BeforeClass public static void init() { System.out.println("Hello Test"); System.out.println("Test runs at " + new Date()); }
3. @AfterClass
Этот метод запускается один раз после всех тест-кейсов. Используйте его для задач очистки, таких как закрытие файлов или соединений.
@AfterClass public static void cleanup() { System.out.println("All tests are done!"); }
Пример, иллюстрирующий каждую аннотацию:
import org.junit.*; public class BeforeAfterClassExample { @BeforeClass public static void setUpClass() { System.out.println("This runs ONCE BEFORE all test cases."); System.out.println("Example: Opening a database connection."); } @AfterClass public static void tearDownClass() { System.out.println("This runs ONCE AFTER all test cases."); System.out.println("Example: Closing the database connection."); } @Test public void testCase1() { System.out.println("Running test case 1."); } @Test public void testCase2() { System.out.println("Running test case 2."); } }
Вывод:
This runs ONCE BEFORE all test cases. Example: Opening a database connection. Running test case 1. Running test case 2. This runs ONCE AFTER all test cases. Example: Closing the database connection.
4. @Before
Этот метод запускается перед каждым тест-кейсом. Он отлично подходит для сброса условий перед каждым тестом.
@Before public void setUp() { System.out.println("Setting up for a test..."); }
5. @After
Этот метод запускается после каждого тест-кейса. Используйте его для очистки после каждого теста.
@After public void tearDown() { System.out.println("Cleaning up after a test..."); }
Пример, иллюстрирующий каждую аннотацию:
import org.junit.*; public class BeforeAfterExample { @Before public void setUp() { System.out.println("This runs BEFORE EACH test case."); System.out.println("Example: Initializing test data."); } @After public void tearDown() { System.out.println("This runs AFTER EACH test case."); System.out.println("Example: Cleaning up test data."); } @Test public void testCase1() { System.out.println("Running test case 1."); } @Test public void testCase2() { System.out.println("Running test case 2."); } }
Вывод:
This runs BEFORE EACH test case. Example: Initializing test data. Running test case 1. This runs AFTER EACH test case. Example: Cleaning up test data. This runs BEFORE EACH test case. Example: Initializing test data. Running test case 2. This runs AFTER EACH test case. Example: Cleaning up test data.
Пример со всеми аннотациями:
Вот пример, в котором сочетаются @BeforeClass, @AfterClass, @Before и @After:
import org.junit.*; public class CombinedBeforeAfterExample { @BeforeClass public static void setUpClass() { System.out.println("This runs ONCE BEFORE all test cases."); System.out.println("Example: Setting up global resources."); } @AfterClass public static void tearDownClass() { System.out.println("This runs ONCE AFTER all test cases."); System.out.println("Example: Releasing global resources."); } @Before public void setUp() { System.out.println("This runs BEFORE EACH test case."); System.out.println("Example: Preparing test data."); } @After public void tearDown() { System.out.println("This runs AFTER EACH test case."); System.out.println("Example: Cleaning up test data."); } @Test public void testCase1() { System.out.println("Running test case 1."); } @Test public void testCase2() { System.out.println("Running test case 2."); } }
Вывод:
This runs ONCE BEFORE all test cases. Example: Setting up global resources. This runs BEFORE EACH test case. Example: Preparing test data. Running test case 1. This runs AFTER EACH test case. Example: Cleaning up test data. This runs BEFORE EACH test case. Example: Preparing test data. Running test case 2. This runs AFTER EACH test case. Example: Cleaning up test data. This runs ONCE AFTER all test cases. Example: Releasing global resources.
6. @Test(timeout)
Эта аннотация гарантирует, что тест не займет слишком много времени. Если он превышает указанное время (в миллисекундах), тест падает.
@Test(timeout = 2000) public void testTimeout() { // Test logic here }
Что нового в JUnit 5
JUnit 5 — это последняя версия, в которой появилось несколько новых возможностей. Она состоит из трех частей:
- Платформа JUnit: Запускает тесты на JVM.
- JUnit Jupiter: Предоставляет новые аннотации и возможности.
- JUnit Vintage: Поддерживает старые тесты JUnit 3 и 4.
Новые аннотации в JUnit 5:
1. @BeforeAll
Заменяет @BeforeClass. Выполняется один раз перед всеми тест-кейсами.
@BeforeAll public static void init() { System.out.println("Initializing tests..."); }
2. @AfterAll
Заменяет @AfterClass. Выполняется один раз после всех тест-кейсов.
@AfterAll public static void cleanup() { System.out.println("Tests are complete!"); }
3. @BeforeEach
Заменяет @Before. Выполняется перед каждым тест-кейсом.
@BeforeEach public void setUp() { System.out.println("Preparing for a test..."); }
4. @AfterEach
Заменяет @After. Выполняется после каждого тест-кейса.
@AfterEach public void tearDown() { System.out.println("Cleaning up..."); }
5. @DisplayName
Аннотация @DisplayName позволяет давать тест-кейсам кастомные, удобочитаемые имена. Это делает репорты о тестировании более описательными и понятными.
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class DisplayNameExampleTest { @Test @DisplayName("Check if two numbers are equal") public void testEquals() { assertEquals(10, 5 + 5, "5 + 5 should equal 10"); } @Test @DisplayName("Verify if a string is empty") public void testStringIsEmpty() { String str = ""; assertTrue(str.isEmpty(), "The string should be empty"); } @Test @DisplayName("Ensure array contains correct elements") public void testArrayContents() { int[] actual = {1, 2, 3}; int[] expected = {1, 2, 3}; assertArrayEquals(expected, actual, "Arrays should match"); } }
Вывод в отчетах:
При выполнении этих тестов в результатах тестирования вместо имени метода будет отображаться @DisplayName. Например:
- Вместо
testEquals()
вы увидите: «Проверить, равны ли два числа». - Вместо
testStringIsEmpty()
вы увидите: «Проверить, пуста ли строка».
6. @Disabled
Пропускает тест-кейс.
@Test @Disabled("This test is not ready yet") public void skippedTest() { // Test logic here }
7. @Tag
Аннотация @Tag используется для группировки связанных тестов. Это особенно полезно, когда вы хотите запустить определенные группы тестов (например, «Регрессия», «Дымовые», «Интеграционные»), не запуская весь набор.
- Назначение: Используется для группировки или категоризации тестов.
- Пример использования: помогает организовать тесты в логические группы (например, «Регрессия», «Дым», «Интеграция»), чтобы можно было запускать специальные наборы.
- Пример: Если у вас есть 100 тестов, вы можете пометить 20 из них как «Дымовые» и запускать только эти тесты по мере необходимости.
- Ключевой момент: Теги функциональны и используются для фильтрации тестов.
import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; public class TagExampleTest { @Test @Tag("Regression") public void testAddition() { assertEquals(5, 2 + 3, "2 + 3 should equal 5"); } @Test @Tag("Smoke") public void testSubtraction() { assertEquals(2, 5 - 3, "5 - 3 should equal 2"); } @Test @Tag("Integration") public void testMultiplication() { assertEquals(15, 5 * 3, "5 * 3 should equal 15"); } }
Как запускать тесты с тегами:
Вы можете запускать тесты с определенными тегами с помощью вашей IDE или инструментов сборки, таких как Maven или Gradle.
Maven:
mvn test -Dgroups="Regression"
Gradle:
./gradlew test - tests "*TagExampleTest" -PincludeTags="Regression"
IDE (IntelliJ/Eclipse):
Большинство IDE позволяют фильтровать тесты по тегам в тест-раннере.
Комбинирование @Tag и @DisplayName
Вы можете использовать обе аннотации вместе, чтобы сделать ваши тесты еще более организованными и читаемыми.
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class CombinedExampleTest { @Test @Tag("Regression") @DisplayName("Test addition of two positive numbers") public void testPositiveAddition() { assertEquals(8, 3 + 5, "3 + 5 should equal 8"); } @Test @Tag("Smoke") @DisplayName("Test subtraction with negative result") public void testNegativeSubtraction() { assertEquals(-2, 3 - 5, "3 - 5 should equal -2"); } @Test @Tag("Integration") @DisplayName("Check if a list contains specific items") public void testListContents() { assertTrue(List.of(1, 2, 3).contains(2), "List should contain 2"); } }
Выполнение тестов с тегами и отображаемыми именами
При выполнении приведенных выше тестов вывод будет выглядеть примерно так:
CombinedExampleTest ✔ Test addition of two positive numbers [Regression] ✔ Test subtraction with negative result [Smoke] ✔ Check if a list contains specific items [Integration]
Утверждения в JUnit:
Утверждения используются для проверки соответствия фактического результата ожидаемому. Вот некоторые распространенные утверждения:
1. assertArrayEquals
Проверяет, равны ли два массива.
int[] actual = {1, 2, 3, 4}; int[] expected = {1, 2, 3, 4}; Assertions.assertArrayEquals(expected, actual);
2. assertEquals
Проверяет, равны ли два значения.
Assertions.assertEquals(5, 2 + 3);
3. assertTrue / assertFalse
Проверяет, истинно или ложно условие.
Assertions.assertTrue(10 > 5); Assertions.assertFalse(10 < 5);
4. assertNull / assertNotNull
Проверяет, является ли объект нулевым или не нулевым.
String str = null; Assertions.assertNull(str); Assertions.assertNotNull("Hello");
5. assertThrows
Проверяет, было ли выброшено определенное исключение.
Assertions.assertThrows(RuntimeException.class, () -> { throw new RuntimeException(); });
Чем хорош JUnit?
- Повышает качество кода: Тесты помогают выявить ошибки на ранней стадии.
- Экономия времени: автоматизированные тесты быстрее, чем ручное тестирование.
- Простота использования: Аннотации упрощают написание тестов.
- Поддержка CI/CD: хорошо интегрируется с такими инструментами, как Jenkins.
Резюме
Независимо от того, используете ли вы JUnit 4 или 5, аннотации и утверждения упрощают написание и управление тестами.