предисловие
Модульное тестирование — неотъемлемая часть разработки программного обеспечения, но в обычной разработке его часто предпочитают игнорировать из-за плотного проектного цикла и большой рабочей нагрузки, что часто приводит к бесконечным проблемам с программным обеспечением. На самом деле, многие проблемы, которые появляются в сети, можно обнаружить и своевременно решить с помощью модульных тестов, поэтому необходимо развивать свою способность писать модульные тесты в повседневной разработке. Независимо от того, хотите ли вы улучшить свои навыки кодирования или улучшить качество проекта, это принесет большую пользу.Эта статья познакомит вас с основами понимания и использования среды модульного тестирования Java JUnit 5 для написания модульных тестов.Я надеюсь, что она также поможет вам .
Все фрагменты кода, задействованные в этой статье, находятся на следующем складе. Заинтересованные партнеры могут ознакомиться с ними и изучить их:
GitHub.com/ Как будто впервые увидел 12138…
Поддержка версий:
- JDK 8
- JUnit 5.5.2
- Lomok 1.18.8
Знакомство с JUnit 5.
Чтобы говорить о JUnit 5, мы должны сначала поговорить о среде модульного тестирования Java JUnit.Он и другая среда, TestNG, занимают основной рынок сред модульного тестирования в области Java.Среди них JUnit имеет долгую историю развития и богатые functions. , предпочитаемый большинством разработчиков Java.
Что касается истории JUnit, JUnit возник в 1997 г. Первоначальная версия была завершена двумя мастерами программирования Кентом Беком и Эрихом Гаммой во время полета на самолете.Из-за отсутствия зрелых инструментов в процессе тестирования Java в то время, два были в самолете.Что касается совместной разработки и реализации прототипа JUnit, он стремится стать лучшей средой тестирования Java. По прошествии более 20 лет JUnit итеративно развивался через различные версии и был разработан до версии 5.x, обеспечивающей лучшую поддержку (например, поддержку Lambda) и более богатые формы тестирования (например, JDK 8 и более поздние версии). , параметризованные тесты).
Узнав о JUint, давайте вернемся к JUnit 5. Эту версию можно назвать крупным обновлением среды модульного тестирования JUnit. Во-первых, требуется работающая среда выше Java 8. Хотя старую версию JDK также можно скомпилировать и запустить, он должен быть полностью использован.Функциональность JUnit 5, среда JDK 8 имеет важное значение.
Помимо этого, JUnit 5 отличается от предыдущих версий JUnit тем, что он разделен на несколько разных модулей, состоящих из трех разных подпроектов.
JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage
-
JUnit Platform: базовая служба, используемая для запуска среды тестирования на JVM, обеспечивающая поддержку выполнения тестов в командной строке, среде IDE и инструментах сборки.
-
JUnit Jupiter: Содержит новую модель программирования и модель расширения JUnit 5, в основном для написания тестового кода и кода расширения.
-
JUnit Vintage: для запуска тестовых случаев JUnit3.x и JUnit4.x в JUnit 5, совместимых.
Основываясь на приведенном выше введении, вы можете обратиться к следующему рисунку, чтобы понять архитектуру и модули JUnit 5:
Зачем вам JUnit 5
После разговора о том, что такое JUnit 5, давайте подумаем над другим вопросом: зачем нам нужен JUnit 5?
С появлением таких сред тестирования, как JUnit, область модульного тестирования Java постепенно созрела, и разработчики предъявляют более высокие требования к средам модульного тестирования: больше методов тестирования и меньше зависимостей от других библиотек. Поэтому все с нетерпением ждут рождения более мощной среды тестирования.Являясь лидером в области тестирования Java, компания JUnit выпустила версию JUnit 5. Основные функции:
-
Предоставляет новые утверждения и тестовые аннотации для поддержки встроенных тестовых классов.
-
Более богатые методы тестирования: поддержка динамического тестирования, повторного тестирования, параметризованного тестирования и т. д.
-
Реализована модульность, отделяющая различные модули, такие как выполнение тестов и обнаружение тестов, а также уменьшающая количество зависимостей.
-
Обеспечивает поддержку Java 8, например лямбда-выражений, Sream API и т. д.
Введение в общее использование JUnit 5
Далее давайте рассмотрим некоторые распространенные варианты использования JUni 5, которые помогут нам быстро освоить использование JUnit 5.
В первую очередь в проект Maven вводятся координаты зависимостей JUnit 5. Следует отметить, что текущая среда JDK должна быть выше Java 8.
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.5.2</version>
<scope>test</scope>
</dependency>
первый тест
Представляя JUnit 5, мы можем быстро написать простой тестовый пример, из этого тестового примера понять начальный JUnit 5:
@DisplayName("我的第一个测试用例")
public class MyFirstTestCaseTest {
@BeforeAll
public static void init() {
System.out.println("初始化数据");
}
@AfterAll
public static void cleanup() {
System.out.println("清理数据");
}
@BeforeEach
public void tearup() {
System.out.println("当前测试方法开始");
}
@AfterEach
public void tearDown() {
System.out.println("当前测试方法结束");
}
@DisplayName("我的第一个测试")
@Test
void testFirstTest() {
System.out.println("我的第一个测试开始测试");
}
@DisplayName("我的第二个测试")
@Test
void testSecondTest() {
System.out.println("我的第二个测试开始测试");
}
}
Запустите этот тестовый пример напрямую, вы можете увидеть журнал консоли следующим образом:
Вы можете видеть, что имя элемента теста, отображаемое в результатах в левом столбце, — это то, что мы используем в тестовом классе и методе.@DisplayNameИмя параметра. Эта аннотация введена в JUnit 5 для определения тестового класса и указания отображаемого имени варианта использования в отчете о тестировании. Эту аннотацию можно использовать в классах и методах. Использование ее в классе указывает, что class является тестовым Class, используемый в методе, указывает, что метод является тестовым методом.
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@API(status = STABLE, since = "5.0")
public @interface DisplayName {
String value();
}
Давайте взглянем на пару аннотаций, используемых в примере кода **@BeforeAll ** и@AfterAll , они определяют работу всего тестового класса до и в конце и могут изменять только статические методы, которые в основном используются для инициализации и очистки глобальных данных и внешних ресурсов, необходимых в процессе тестирования. В отличие от них,@BeforeEachи@AfterEachПомеченный метод будет выполняться до и в конце каждого метода тестового примера, в основном отвечая за подготовку и уничтожение рабочей среды, необходимой для тестового примера.
В дополнение к этим основным аннотациям в процессе тестирования есть более богатые и мощные аннотации, давайте изучим их одну за другой.
Отключить выполнение тестов: @Disabled
Когда мы хотим пропустить тестовый метод и запустить другие тестовые случаи в обычном режиме при запуске тестового класса, мы можем использовать аннотацию @Disabled, чтобы указать, что тестовый метод недоступен и не будет выполняться при выполнении тестового метода тестового класса. Выполнение JUnit.
Давайте посмотрим на эффект запуска после использования @Disbaled и добавим следующий код в исходный тестовый класс:
@DisplayName("我的第三个测试")
@Disabled
@Test
void testThirdTest() {
System.out.println("我的第三个测试开始测试");
}
После запуска вы можете увидеть журнал консоли следующим образом.Метод, помеченный @Disabled, не будет выполнен, и будет напечатана только информация об отдельном методе:
@Disabled также можно использовать в классах, чтобы пометить все тестовые методы в классе от выполнения, что обычно используется при объединении тестов для нескольких тестовых классов.
Вложенный тестовый класс: @Nested
По мере того, как мы будем писать все больше и больше классов и кодов, будет появляться все больше и больше соответствующих тестовых классов, которые необходимо протестировать. Чтобы решить проблему резкого увеличения числа тестовых классов, JUnit 5 предоставляет аннотацию @Nested, которая может логически группировать классы тестовых случаев в виде статических внутренних классов-членов. И каждый статический внутренний класс может иметь свои собственные методы жизненного цикла, которые будут выполняться в порядке от внешней к внутренней иерархии. Кроме того, вложенные классы также могут быть помечены @DisplayName, чтобы мы могли использовать правильное имя теста. Вот простое использование:
@DisplayName("内嵌测试类")
public class NestUnitTest {
@BeforeEach
void init() {
System.out.println("测试方法执行前准备");
}
@Nested
@DisplayName("第一个内嵌测试类")
class FirstNestTest {
@Test
void test() {
System.out.println("第一个内嵌测试类执行测试");
}
}
@Nested
@DisplayName("第二个内嵌测试类")
class SecondNestTest {
@Test
void test() {
System.out.println("第二个内嵌测试类执行测试");
}
}
}
После запуска всех тестовых случаев вы можете увидеть в консоли следующие результаты:
Повторный тест: @RepeatedTest
В JUnit 5 была добавлена поддержка установки количества запусков тестового метода, что позволяет многократно запускать тестовый метод. Если вы хотите запустить тестовый метод N раз, вы можете пометить его @RepeatedTest, как показано в следующем коде:
@DisplayName("重复测试")
@RepeatedTest(value = 3)
public void i_am_a_repeated_test() {
System.out.println("执行测试");
}
После запуска тестовый метод будет выполнен 3 раза, и эффект выполнения в IDEA показан на следующем рисунке:
Это основное использование, мы также можем изменить имя метода тестирования, который запускается повторно, используя встроенные переменные, предоставленные @RepeatedTest, в виде заполнителей в егоnameИспользуйте на свойствах, давайте посмотрим на использование и эффекты:
@DisplayName("自定义名称重复测试")
@RepeatedTest(value = 3, name = "{displayName} 第 {currentRepetition} 次")
public void i_am_a_repeated_test_2() {
System.out.println("执行测试");
}
Используется внутри аннотации @RepeatedTestcurrentRepetitionПеременная представляет количество повторений,totalRepetitionsПеременная представляет собой общее количество повторений, которые необходимо повторить,displayNameПеременные представляют отображаемое имя метода тестирования, и мы можем использовать эти встроенные переменные для переопределения имени метода тестирования при его повторном запуске.
новое утверждение
Что касается дизайна API утверждений, в JUnit 5 были внесены значительные улучшения, и он в полной мере использует новые функции Java 8, особенно лямбда-выражения, и, наконец, предоставляет новые классы утверждений:org.junit.jupiter.api.Assertions. Многие методы утверждений принимают параметры лямбда-выражений.Одно из преимуществ использования лямбда-выражений в сообщениях утверждений заключается в том, что они вычисляются лениво, что может в определенной степени сэкономить время и ресурсы, если создание сообщения является дорогостоящим.
Теперь также можно сгруппировать несколько утверждений внутри метода, используя метод assertAll следующим образом:
@Test
void testGroupAssertions() {
int[] numbers = {0, 1, 2, 3, 4};
Assertions.assertAll("numbers",
() -> Assertions.assertEquals(numbers[1], 1),
() -> Assertions.assertEquals(numbers[3], 3),
() -> Assertions.assertEquals(numbers[4], 4)
);
}
Если какое-либо из сгруппированных утверждений не выполняется, будет выдана ошибка MultipleFailuresError.
Тест на тайм-аут: assertTimeoutPreemptively
Когда мы хотим проверить время выполнения трудоемкого метода и не хотим, чтобы тестовый метод ждал бесконечно, мы можем выполнить тест тайм-аута для тестового метода.JUnit 5 вводит метод утверждения.assertTimeout, который обеспечивает расширенную поддержку тайм-аутов.
Предположим, мы хотим, чтобы тестовый код выполнялся за одну секунду, мы можем написать следующий тестовый пример:
@Test
@DisplayName("超时方法测试")
void test_should_complete_in_one_second() {
Assertions.assertTimeoutPreemptively(Duration.of(1, ChronoUnit.SECONDS), () -> Thread.sleep(2000));
}
Этот тест не выполняется, потому что выполнение кода будет приостановлено на две секунды, и мы ожидаем, что тестовый пример завершится успешно в течение одной секунды. Однако, если мы установим время ожидания в одну секунду, тест все равно иногда будет давать сбои, потому что выполнение дополнительного кода и инструкций в дополнение к целевому коду во время выполнения тестового метода потребует времени, поэтому его нельзя сделано на пределе тайм-аута до точного точного совпадения с параметром времени.
Тестирование исключений: assertThrows
Для методов с исключениями в нашем коде мы обычно используем try-catch для их захвата и обработки.Для тестирования кода с выброшенными исключениями JUnit 5 предоставляет методыAssertions#assertThrows(Class<T>, Executable)Для тестирования первый параметр — это тип исключения, а второй — параметр функционального интерфейса. Как и в случае с интерфейсом Runnable, он не требует параметров и не возвращает значения, а также поддерживает использование лямбда-выражений. Подробнее см. следующий код:
@Test
@DisplayName("测试捕获的异常")
void assertThrowsException() {
String str = null;
Assertions.assertThrows(IllegalArgumentException.class, () -> {
Integer.valueOf(str);
});
}
Когда в коде в лямбда-выражении возникает исключение, оно будет сравниваться с типом исключения первого параметра.Если оно не принадлежит к тому же типу исключения, консоль выведет запрос, подобный следующему:org.opentest4j.AssertionFailedError: Unexpected exception type thrown ==> expected: <IllegalArgumentException> but was: <...Exception>
Параметризованные тесты JUnit 5
Чтобы использовать JUnit 5 для параметризованных тестов, в дополнение к базовой зависимости junit-jupiter-engine требуется еще одна зависимость модуля:junit-jupiter-params, который в основном предоставляет API для написания параметризованных тестов. Таким же образом импортируйте соответствующие зависимости той же версии в проект Maven:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.5.2</version>
<scope>test</scope>
</dependency>
Базовый тест источника данных: @ValueSource
@ValueSource — это простейший источник параметров данных, предоставляемый JUnit 5. Он поддерживает восемь основных типов Java и строк, класс. При использовании он назначается соответствующему атрибуту типа в аннотации и передается в виде массива. Пример кода выглядит следующим образом: следует:
public class ParameterizedUnitTest {
@ParameterizedTest
@ValueSource(ints = {2, 4, 8})
void testNumberShouldBeEven(int num) {
Assertions.assertEquals(0, num % 2);
}
@ParameterizedTest
@ValueSource(strings = {"Effective Java", "Code Complete", "Clean Code"})
void testPrintTitle(String title) {
System.out.println(title);
}
}
@ParameterizedTest заменяет аннотацию @Test как обязательную аннотацию для параметризованных тестов. Любой параметризованный метод тестирования должен быть помечен этой аннотацией.
Запустите тест, и результат показан на рисунке ниже. Целевой метод будет запущен для каждого параметра в @ValueSource. Если параметр не может запустить тест, это означает, что метод теста не работает.
Тест источника данных CSV: @CsvSource
Через @CsvSource вы можете вводить набор данных в указанном формате CSV (значения, разделенные запятыми) и использовать каждое значение, разделенное запятыми, для соответствия параметру, соответствующему методу тестирования. Ниже приведен пример использования:
@ParameterizedTest
@CsvSource({"1,One", "2,Two", "3,Three"})
void testDataFromCsv(long id, String name) {
System.out.printf("id: %d, name: %s", id, name);
}
Текущий результат показан на рисунке.В дополнение к разделению параметров запятыми, @CsvSource также поддерживает пользовательские символы, если вы изменяете егоdelimiterОК, по умолчанию,.
JUnit также предоставляет способ чтения данных файла внешнего формата CSV в качестве реализации источника данных.Нам нужно только указать путь к файлу ресурсов с помощью @CsvFileSource.Это так же просто, как и @CsvSource, и здесь повторяться не будем.
Путь к файлу ресурсов, указанный @CsvFileSource, должен начинаться с
/Для начала найдите файлы в текущем каталоге тестовых ресурсов.
В дополнение к трем методам источника данных, упомянутым выше, JUnit также предоставляет следующие три источника данных:
- @EnumSource: позволяет нам передать значение параметра в указанный тип перечисления Enum для создания определенного значения в типе перечисления.
- @MethodSource: укажите метод, который возвращает объект Stream/Array/Iterable в качестве источника данных. Обратите внимание, что метод должен быть статическим и не может принимать никаких параметров.
-
@ArgumentSource: использовать класс параметров, реализующий интерфейс ArgumentsProvider, в качестве источника данных и переопределить его.
provideArgumentsМетод может возвращать Streamпользовательского типа, который можно использовать в качестве данных, требуемых тестовым методом.
Учащиеся, которым интересны приведенные выше три аннотации к источникам данных, могут обратиться к классу ParameterizedUnitTest примера проекта, который не будет вводиться здесь снова и снова.
Эпилог
На этом этапе вы должны иметь базовое понимание и мастерство JUnit 5. Говорят, что модульное тестирование является важным звеном для улучшения качества программного обеспечения и повышения эффективности исследований и разработок.Начните с написания модульных тестов с помощью JUnit 5 и выработайте привычку писать тестовый код.Повышайте эффективность собственной разработки на постоянной практике, чтобы код, который вы пишете, имел лучшую гарантию качества.
Рекомендуемое чтение
- Одна статья, чтобы освоить Spring Boot Profiles
- Как изящно закрыть приложение Spring Boot
- Вам нужно управление интерфейсом?
- Ломбок из Java должен знать, должен знать
- Nacos нового поколения микросервисов Java
использованная литература
-
Модульное тестирование Java с JUnit 5