Java JUnit — фреймворк модульного тестирования

Java 常用类库Общие библиотеки Java


JUnit — один из самых популярных фреймворков модульного тестирования для языка программирования Java, используемый для написания и запуска повторяемых автоматизированных тестов.

JUnit был создан Кентом Беком и Эрихом Гаммой и является представителем семейства xUnit. Модульное тестирование — это процесс проверки и верификации наименьших тестируемых частей программного обеспечения, обычно методов или классов.

Основные возможности JUnit включают:

  • Предоставляет аннотации для идентификации тестовых методов
  • Предоставляет утверждения для проверки ожидаемых результатов
  • Поддерживает тестовые наборы
  • Предоставляет средство запуска тестов

Зачем нужно модульное тестирование?

Модульное тестирование — неотъемлемая часть современной разработки программного обеспечения. Оно даёт следующие преимущества:

  1. Раннее обнаружение проблем: Обнаруживайте и исправляйте ошибки во время разработки
  2. Улучшенное качество кода: Побуждает разработчиков писать более модульный, тестируемый код
  3. Ценность документации: Тестовые примеры — лучшая документация поведения кода
  4. Страховочная сеть при рефакторинге: Гарантирует, что изменение кода не нарушает существующую функциональность
  5. Сокращение времени отладки: Быстрое обнаружение проблем

Использование JUnit в проектах

Для JUnit 5:

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.8.2</version>
    <scope>test</scope>
</dependency>

Для JUnit 4:

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
    <scope>test</scope>
</dependency>

Основы JUnit 5

JUnit 5 — последняя версия, состоящая из трёх основных модулей:

  • JUnit Platform (основа для запуска тестов)
  • JUnit Jupiter (новая модель программирования и модель расширений)
  • JUnit Vintage (поддерживает запуск тестов JUnit 3 и 4)

Базовые аннотации

Устаревшие аннотации:

  • @Test: Помечает метод как тестовый метод

  • @Before: Выполняется перед каждым тестовым методом

  • @After: Выполняется после каждого тестового метода

  • @BeforeClass: Выполняется перед всеми тестами (статический метод)

  • @AfterClass: Выполняется после всех тестов (статический метод)

  • @Ignore: Игнорирует тестовый метод

Пример

import org.junit.*;

public class CalculatorTest {
   
    @BeforeClass
    public static void setUpBeforeClass() {
        System.out.println("Перед всеми тестами");
    }
   
    @Before
    public void setUp() {
        System.out.println("Перед каждым тестом");
    }
   
    @Test
    public void testAddition() {
        Calculator calculator = new Calculator();
        int result = calculator.add(2, 3);
        Assert.assertEquals(5, result);
    }
   
    @Test
    @Ignore("Ещё не реализовано")
    public void testSubtraction() {
        // тестовый код
    }
   
    @After
    public void tearDown() {
        System.out.println("После каждого теста");
    }
   
    @AfterClass
    public static void tearDownAfterClass() {
        System.out.println("После всех тестов");
    }
}

JUnit 5 — это версия нового поколения со следующими новыми аннотациями:

  • @BeforeEachзаменяет@Before

  • @AfterEachзаменяет@After

  • @BeforeAllзаменяет@BeforeClass

  • @AfterAllзаменяет@AfterClass

  • @Disabledзаменяет@Ignore

Пример

import org.junit.jupiter.api.*;

@Test
void testMethod() {
    // тестовый код
}

@BeforeEach
void setUp() {
    // Выполняется перед каждым тестовым методом
}

@AfterEach
void tearDown() {
    // Выполняется после каждого тестового метода
}

@BeforeAll
static void initAll() {
    // Выполняется один раз перед всеми тестовыми методами
}

@AfterAll
static void tearDownAll() {
    // Выполняется один раз после всех тестовых методов
}

Общие методы утверждений

JUnit предоставляет богатый набор методов утверждений для проверки результатов тестов:

  • assertEquals(expected, actual)

  • assertTrue(condition)

  • assertFalse(condition)

  • assertNull(object)

  • assertNotNull(object)

  • assertSame(expected, actual)(Проверяет, является ли это тем же объектом)

  • assertNotSame(unexpected, actual)

  • assertArrayEquals(expectedArray, actualArray)

Пример

import static org.junit.jupiter.api.Assertions.*;

@Test
void testAssertions() {
    // Утверждение равенства
    assertEquals(expected, actual);
   
    // Утверждение истинности
    assertTrue(condition);
   
    // Утверждение null
    assertNull(object);
   
    // Утверждение исключения
    assertThrows(ExpectedException.class, () -> {
        // Код, который выбрасывает исключение
    });
   
    // Проверка тайм-аута
    assertTimeout(Duration.ofMillis(100), () -> {
        // Код, который должен завершиться за указанное время
    });
}

Написание вашего первого теста JUnit

Давайте научимся писать тесты JUnit на простом примере.

1. Создайте тестируемый класс

Пример

public class Calculator {
    public int add(int a, int b) {
        return a + b;
    }
   
    public int divide(int a, int b) {
        if (b == 0) {
            throw new ArithmeticException("Divisor cannot be zero");
        }
        return a / b;
    }
}

2. Создайте тестовый класс

Пример

import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;

class CalculatorTest {
    private Calculator calculator;
   
    @BeforeEach
    void setUp() {
        calculator = new Calculator();
    }
   
    @Test
    void testAdd() {
        assertEquals(5, calculator.add(2, 3));
    }
   
    @Test
    void testDivide() {
        assertEquals(2, calculator.divide(6, 3));
    }
   
    @Test
    void testDivideByZero() {
        assertThrows(ArithmeticException.class, () -> {
            calculator.divide(1, 0);
        });
    }
}

Расширенные возможности JUnit

Параметризованные тесты

JUnit 5 предоставляет@ParameterizedTestаннотация, позволяющая запускать один и тот же тест несколько раз с разными параметрами:

Пример

@ParameterizedTest
@ValueSource(ints = {1, 2, 3})
void testWithValueSource(int argument) {
    assertTrue(argument > 0 && argument < 4);
}

Жизненный цикл теста

Понимание жизненного цикла тестов JUnit важно для написания эффективных тестов:

  1. @BeforeAllВыполнение метода (только один раз)
  2. Для каждого метода теста:
    • Создание экземпляра тестового класса
    • @BeforeEachВыполнение метода
    • Выполнение метода теста
    • @AfterEachВыполнение метода
  3. @AfterAllВыполнение метода (только один раз)

Тестовые наборы

Вы можете использовать@Suiteаннотация для объединения нескольких тестовых классов для совместного запуска:

Пример

import org.junit.platform.suite.api.SelectClasses;
import org.junit.platform.suite.api.Suite;

@Suite
@SelectClasses({CalculatorTest.class, AnotherTest.class})
public class TestSuite {
}

Лучшие практики

  1. Именование тестов: Имена тестовых методов должны ясно выражать их назначение, напримерshouldReturnTrueWhenInputIsValid()
  2. Единственная ответственность: Каждый тестовый метод должен проверять только одну функциональность
  3. Независимые тесты: Тесты не должны зависеть друг от друга
  4. Быстрая обратная связь: Поддерживайте быстрое выполнение тестов
  5. Покрытие тестами: Стремитесь к разумному покрытию, но не гонитесь слепо за 100%
  6. Тестовые данные: Используйте осмысленные тестовые данные
  7. Избегайте тестирования деталей реализации: Тестируйте поведение, а не реализацию

Часто задаваемые вопросы

В чём разница между JUnit 4 и JUnit 5?

Основные различия включают:

  • JUnit 5 требует Java 8 или выше
  • Аннотации перемещены изorg.junitпакета вorg.junit.jupiter.apiПакет
  • @Beforeи@Afterизменено на@BeforeEachи@AfterEach
  • @BeforeClassи@AfterClassизменено на@BeforeAllи@AfterAll
  • JUnit 5 вводит модель расширений для замены правил JUnit 4

Как запускать тесты JUnit?

Запускать тесты JUnit можно следующими способами:

  1. Щёлкните правой кнопкой мыши по тестовому классу или методу в IDE и выберите "Run"
  2. Использование Maven:mvn test
  3. Использование Gradle:gradle test
  4. Использование JUnit Console Launcher

Как тестировать приватные методы?

Обычно не рекомендуется тестировать приватные методы напрямую; вместо этого тестируйте их косвенно через публичные методы. Если всё же необходимо тестировать приватные методы, рассмотрите следующее:

  1. Использование рефлексии
  2. Измените метод на package-private (без модификатора) и создайте тестовый класс с той же структурой пакетов в тестовом каталоге
  3. Отрефакторите код, чтобы вынести логику приватного метода в отдельный класс

Итог

JUnit — важный инструмент тестирования для Java-разработчиков. Его освоение может значительно повысить качество кода и эффективность разработки. Начните с простых модульных тестов и постепенно изучайте более продвинутые возможности, такие как параметризованные тесты и тестовые наборы. Помните, что хорошие тесты должны цениться так же, как продакшен-код; поддерживать тестовый код чистым и удобным для сопровождения столь же важно.

Java 常用类库Общие библиотеки Java

Другие расширения