SpringBoot бой: JUnit5+MockMvc+Mockito проводят модульное тестирование

Spring Boot задняя часть
SpringBoot бой: JUnit5+MockMvc+Mockito проводят модульное тестирование

«Это 29-й день моего участия в ноябрьском испытании обновлений. Подробную информацию об этом событии см.:Вызов последнего обновления 2021 г."

Эта статья была«Весенняя загрузка в действии»Коллекция столбцов.

Привет, я смотрю на гору.

Сегодня поговорим о том, как интегрировать Junit5, MockMvc и Mocktio в SpringBoot. Junit5 является наиболее широко используемой средой тестирования в стеке Java, и Junit4 когда-то доминировал в этом списке.

После обновления до Junit5, помимо добавления многих функций Java8, было сделано множество функциональных улучшений, оптимизирована и скорректирована структура, а также разделено множество различных модулей, которые можно вводить по мере необходимости, например:

  • Платформа JUnit — запуск тестовой среды на JVM.
  • JUnit Jupiter — Написание тестов и расширений в JUnit5
  • JUnit Vintage — предоставляет тестовый движок для запуска тестов на основе JUnit3 и JUnit4.

Начиная с SpringBoot 2.2.0, Junit5 стала версией Junit по умолчанию. Благодаря JUnit Vintage стоимость перехода с Junit4 на Junit5 чрезвычайно низка. Итак, эта статья начинается непосредственно с Junit5.

Версия

Давайте сначала поговорим о версии, чтобы избежать всевозможных странных проблем из-за различий версий:

  • JDK: jdk8 (дополнительную версию можно игнорировать)
  • Спрингбут: 2.5.2
    • наследоватьspring-boot-starter-parent
    • полагатьсяspring-boot-starter-web
    • полагатьсяspring-boot-starter-test
  • Юнит: 5.7.2
  • Мокито: 3.9.0
  • подколенный гребень: 2,2

Преимущество SpringBoot в том, что до тех пор, пока наследованиеspring-boot-starter-parentили представитьspring-boot-dependencies, затем добавьтеspring-boot-starter-testЗависеть от этого. Содержимое определенного POM выглядит следующим образом:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.2</version>
    </parent>
    <groupId>cn.howardliu.effective.spring</groupId>
    <artifactId>springboot-junit5-mockito</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-junit5-mockio</name>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

из-за наследстваspring-boot-starter-parent, поэтому мы полагаемся наspring-boot-starter-testНет необходимости писать конкретную версию, и определение версии родителя может быть напрямую интегрировано. в,spring-boot-starter-webпредставляет собой веб-контейнер для обслуживания REST API,spring-boot-starter-testМогут быть предоставлены различные тестовые среды,spring-boot-maven-plugin— это плагин, который упаковывает приложения SpringBoot в виде исполняемых файлов jar.

Структура проекта

Поскольку это пример DEMO, мы реализуем интерфейс Echo, который может получать параметры запроса и возвращать обработанную строку. По соглашению мы используем всемогущийHello, World!.

Структура нашего проекта выглядит следующим образом:

├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── cn
    │   │       └── howardliu
    │   │           └── effective
    │   │               └── spring
    │   │                   └── springbootjunit5mockio
    │   │                       ├── SpringbootJunit5MockioApplication.java
    │   │                       ├── controller
    │   │                       │   └── EchoController.java
    │   │                       └── service
    │   │                           ├── EchoService.java
    │   │                           └── impl
    │   │                               └── EchoServiceImpl.java
    │   └── resources
    │       └── application.yaml
    └── test
        └── java
            └── cn
                └── howardliu
                    └── effective
                        └── spring
                            └── springbootjunit5mockio
                                └── controller
                                    ├── EchoControllerMockTest.java
                                    └── EchoControllerNoMockitoTest.java

  • SpringbootJunit5MockioApplication: запись запуска приложения SpringBoot
  • EchoController: определение интерфейса
  • EchoService: реализация интерфейса бизнес-логики
  • EchoServiceImpl: реализация интерфейса
  • EchoControllerMockTest: реализовано с использованием фиктивного прокси-сервера EchoService.
  • EchoControllerNoMockitoTest: непосредственная тестовая реализация интерфейса.

EchoServiceImpl

Давайте посмотримEchoService, который будет основной реализацией нашего DEMO:

@Service
public class EchoServiceImpl implements EchoService {
    @Override
    public String echo(String foo) {
        return "Hello, " + foo;
    }
}

EchoControllerNoMockitoTest

Сначала мы используем Junit5+MockMvc для реализации обычного вызова интерфейса Controller, код выглядит следующим образом:

@SpringBootTest(classes = SpringbootJunit5MockioApplication.class)
@AutoConfigureMockMvc
class EchoControllerNoMockitoTest {
    @Autowired
    private MockMvc mockMvc;

    @Test
    void echo() throws Exception {
        final String result = mockMvc.perform(
                MockMvcRequestBuilders.get("/echo/")
                        .param("name", "看山")
        )
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print())
                .andReturn()
                .getResponse()
                .getContentAsString(StandardCharsets.UTF_8);

        Assertions.assertEquals("Hello, 看山", result);
    }
}

мы проходимSpringBootTestАннотация определяет, что это тестовый пример для приложения SpringBoot, а затем передаетAutoConfigureMockMvcЗапустите тестовый контейнер. Таким образом, вы можете напрямую вводитьMockMvcЭкземпляр тестирует интерфейс контроллера.

Здесь следует отметить одну вещь: многие онлайн-учебники позволят вам написать@ExtendWith({SpringExtension.class})Такая заметка на самом деле совершенно не нужна. Из исходного кода мы можем знать,SpringBootTestЗаметки были добавленыExtendWith.

EchoControllerMockTest

В этом тестовом примере мы проксируем через компонент Mockito.EchoServiceизechoметод, код выглядит следующим образом:

@SpringBootTest(classes = SpringbootJunit5MockioApplication.class)
@ExtendWith(MockitoExtension.class)
@AutoConfigureMockMvc
class EchoControllerMockTest {
    @Autowired
    private MockMvc mockMvc;
    @MockBean
    private EchoService echoService;

    @BeforeEach
    void setUp() {
        Mockito.when(echoService.echo(Mockito.any()))
                .thenReturn("看山说:" + System.currentTimeMillis());
    }

    @Test
    void echo() throws Exception {
        final String result = mockMvc.perform(
                MockMvcRequestBuilders.get("/echo/")
                        .param("name", "看山的小屋")
        )
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print())
                .andReturn()
                .getResponse()
                .getContentAsString(StandardCharsets.UTF_8);

        Assertions.assertTrue(result.startsWith("看山"));
    }
}

В этом примере нам нужно обратить внимание на@ExtendWith(MockitoExtension.class)Аннотация, эта аннотация используется для ознакомленияMockBeanДа, мы пройдемechoПерехват метода заставляет его возвращать результат ответа, который мы определили. Этот метод позволяет избежать необходимости фактического вызова интерфейса при тестировании нескольких систем или нескольких функций.

Например, нам нужно получить номер мобильного телефона пользователя.Обычно мы будем проверять, вошел ли пользователь в систему в интерфейсе.Мы можем использовать возможность Mockito проксировать проверку входа, чтобы результат всегда был верным.

Вывод в конце статьи

На данный момент мы завершили пример интеграции SpringBoot с Junit5, MockMvc и Mockito. Если вы хотите получить исходный код, вам просто нужно обратить внимание на публичный аккаунт «Хижина на горе» и ответить «весна».

Многие студенты считают, что нет необходимости писать модульные тесты, и они могут хорошо протестировать интерфейс, напрямую используя такие инструменты, как Swagger или Postman. Ведь для простых CRUD-интерфейсов писать юнит-тесты не очень-то и нужно. А как насчет сложных интерфейсов? Комбинаций параметров интерфейса много, и результаты ответа тоже нуждаются в различных проверках.Если использовать одноразовый инструмент, каждый раз тестировать параметры комбинации, это уже будет вызывать у людей крах, а параметры комбинации не могут быть сохранены или даже передаваться по наследству нескольким людям, что приведет к потере большого количества рабочей силы.

В этот момент проявится эффект модульного тестирования. Нам нужно только один раз написать комбинацию параметров, поместить ее в файл, такой как csv, и запустить ее несколько раз, чтобы проверить правильность интерфейса с помощью параметризованного тестового метода модульного тестирования.

Или, когда мы чувствуем, что система была заполнена запахом, его реконструкция, чтобы проверить те же функции интерфейса, можно напрямую можно проверить с помощью исходного тестового корпуса.

Подводя итог, хотя написание тестовых случаев хлопотно, это бесконечно полезно.

Рекомендуемое чтение


Привет, я смотрю на гору. Плавайте в мире кода, играйте и наслаждайтесь жизнью. Если статья была вам полезна, ставьте лайк, добавляйте в закладки и подписывайтесь. Приглашаем обратить внимание на паблик-аккаунт «Глядя на горную хижину» и открыть для себя другой мир.