有啥不同?來看看Spring Boot 基於 JUnit 5 實現單元測試

雲棲號資訊:【點擊查看更多行業資訊
在這裏您可以找到不同行業的第一手的上雲資訊,還在等什麼,快來!

簡介

Spring Boot 2.2.0 版本開始引入 JUnit 5 作爲單元測試默認庫,在 Spring Boot 2.2.0 版本之前,spring-boot-starter-test 包含了 JUnit 4 的依賴,Spring Boot 2.2.0 版本之後替換成了 Junit Jupiter。

JUnit 4 和 JUnit 5 的差異

  1. 忽略測試用例執行

JUnit 4:

@Test
@Ignore
public void testMethod() {
   // ...
}

JUnit 5:

@Test
@Disabled("explanation")
public void testMethod() {
   // ...
}
  1. RunWith 配置

JUnit 4:

@RunWith(SpringRunner.class)
@SpringBootTest
public class ApplicationTests {
    @Test
    public void contextLoads() {
    }
}

JUnit 5:

@ExtendWith(SpringExtension.class)
@SpringBootTest
public class ApplicationTests {
    @Test
    public void contextLoads() {
    }
}
  1. @Before、@BeforeClass、@After、@AfterClass 被替換

@BeforeEach 替換 @Before
@BeforeAll 替換 @BeforeClass
@AfterEach 替換 @After
@AfterAll 替換 @AfterClass

開發環境

JDK 8

示例

1.創建 Spring Boot 工程。

2.添加 spring-boot-starter-web 依賴,最終 pom.xml 如下。

<?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.2.6.RELEASE</version>
        <relativePath/>
    </parent>
    <groupId>tutorial.spring.boot</groupId>
    <artifactId>spring-boot-junit5</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-boot-junit5</name>
    <description>Demo project for Spring Boot Unit Test with JUnit 5</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

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

</project>

3.工程創建好之後自動生成了一個測試類。

package tutorial.spring.boot.junit5;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class SpringBootJunit5ApplicationTests {

    @Test
    void contextLoads() {
    }

}

這個測試類的作用是檢查應用程序上下文是否可正常啓動。@SpringBootTest 註解告訴 Spring Boot 查找帶 @SpringBootApplication 註解的主配置類,並使用該類啓動 Spring 應用程序上下文。互聯網架構師公衆號內回覆“2T”, 送你全套Java架構視頻

4.補充待測試應用邏輯代碼

4.1. 定義 Service 層接口

package tutorial.spring.boot.junit5.service;

public interface HelloService {

    String hello(String name);
}

4.2. 定義 Controller 層

package tutorial.spring.boot.junit5.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import tutorial.spring.boot.junit5.service.HelloService;

@RestController
public class HelloController {

    private final HelloService helloService;

    public HelloController(HelloService helloService) {
        this.helloService = helloService;
    }

    @GetMapping("/hello/{name}")
    public String hello(@PathVariable("name") String name) {
        return helloService.hello(name);
    }
}

4.3. 定義 Service 層實現

package tutorial.spring.boot.junit5.service.impl;

import org.springframework.stereotype.Service;
import tutorial.spring.boot.junit5.service.HelloService;

@Service
public class HelloServiceImpl implements HelloService {

    @Override
    public String hello(String name) {
        return "Hello, " + name;
    }
}

5.編寫發送 HTTP 請求的單元測試。

package tutorial.spring.boot.junit5;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class HttpRequestTest {

    @LocalServerPort
    private int port;

    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    public void testHello() {
        String requestResult = this.restTemplate.getForObject("http://127.0.0.1:" + port + "/hello/spring",
                String.class);
        Assertions.assertThat(requestResult).contains("Hello, spring");
    }
}

說明:

webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT 使用本地的一個隨機端口啓動服務;

@LocalServerPort 相當於 @Value("${local.server.port}");

在配置了 webEnvironment 後,Spring Boot 會自動提供一個 TestRestTemplate 實例,可用於發送 HTTP 請求。

除了使用 TestRestTemplate 實例發送 HTTP 請求外,還可以藉助 org.springframework.test.web.servlet.MockMvc 完成類似功能,代碼如下:

package tutorial.spring.boot.junit5.controller;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

@SpringBootTest
@AutoConfigureMockMvc
public class HelloControllerTest {

    @Autowired
    private HelloController helloController;

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void testNotNull() {
        Assertions.assertThat(helloController).isNotNull();
    }

    @Test
    public void testHello() throws Exception {
        this.mockMvc.perform(MockMvcRequestBuilders.get("/hello/spring"))
                .andDo(MockMvcResultHandlers.print())
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.content().string("Hello, spring"));
    }
}

以上測試方法屬於整體測試,即將應用上下文全都啓動起來,還有一種分層測試方法,譬如僅測試 Controller 層。

6.分層測試。

package tutorial.spring.boot.junit5.controller;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import tutorial.spring.boot.junit5.service.HelloService;

@WebMvcTest
public class HelloControllerTest {

    @Autowired
    private HelloController helloController;

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private HelloService helloService;

    @Test
    public void testNotNull() {
        Assertions.assertThat(helloController).isNotNull();
    }

    @Test
    public void testHello() throws Exception {
        Mockito.when(helloService.hello(Mockito.anyString())).thenReturn("Mock hello");
        this.mockMvc.perform(MockMvcRequestBuilders.get("/hello/spring"))
                .andDo(MockMvcResultHandlers.print())
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.content().string("Mock hello"));
    }
}

說明:

@WebMvcTest 註釋告訴 Spring Boot 僅實例化 Controller 層,而不去實例化整體上下文,還可以進一步指定僅實例化 Controller 層的某個實例:@WebMvcTest(HelloController.class);

因爲只實例化了 Controller 層,所以依賴的 Service 層實例需要通過 @MockBean 創建,並通過 Mockito 的方法指定 Mock 出來的 Service 層實例在特定情況下方法調用時的返回結果。

【雲棲號在線課堂】每天都有產品技術專家分享!
課程地址:https://yqh.aliyun.com/zhibo

立即加入社羣,與專家面對面,及時瞭解課程最新動態!
【雲棲號在線課堂 社羣】https://c.tb.cn/F3.Z8gvnK

原文發佈時間:2020-08-04
本文作者:又語
本文來自:“互聯網架構師”,瞭解相關信息可以關注“互聯網架構師

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章