1. 程式人生 > 其它 >Spring Boot 整合 JUnit 單元測試

Spring Boot 整合 JUnit 單元測試

技術標籤:spring bootjavaspring bootjunit

為自己的應用編寫單元測試是一個很好的習慣。在Java開發中最流行的測試工具非JUnit莫屬,它已經成為Java單元測試的事實標準。Spring Boot測試模組不僅整合JUnit框架,還提供了許多實用程式和註釋,方便我們測試應用。

1. 新增依賴

在 pom.xml 檔案中引入 spring-boot-starter-test

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>
spring-boot-starter-test</artifactId> <version>${version}</version> <scope>test</scope> </dependency>

Spring Boot 2.2.x 開始整合的是JUnit 5。如果之前是使用的JUnit 4,可以使用JUnit 5中提供的老式引擎執行,需要新增 junit-vintage-engine 依賴。

Spring Boot 2.2.x釋出很久了,現在最新穩定版是2.4.x。舊的總要被替代,所以本篇只用JUnit 5,關於JUnit 4的文章相信網上很多,官方也有給出使用說明,請自行查詢。

2. 編寫單元測試

@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class JUnitTest {

    @Test
    public void test() {
        // 測試程式碼
    }
}

@SpringBootTest 重要引數

  • args
    應用程式引數,如:args = “–app.test=one”

  • classes
    Spring Boot應用啟動入口類名,該引數不指定時由Spring Boot預設查詢。

  • webEnvironment
    預設情況下@SpringBootTest不會啟動伺服器。當測試Web應用時,需指定該引數以便載入上下文環境。

WebEnvironment列舉值說明:

  • MOCK
    預設值,載入WebApplicationContext並提供模擬Web環境。使用此註釋時,不會啟動嵌入式伺服器。

  • RANDOM_PORT
    啟動應用並隨機監聽一個埠。

  • DEFINED_PORT
    啟動應用並監聽自定義的埠(來自application.properties)或使用預設埠8080。

  • NONE
    ApplicationContext通過使用載入,SpringApplication但不提供任何網路環境(模擬或其他方式)。

@Test

注意 JUnit 5 的 @Test 註解在 org.junit.jupiter.api 包下。


如果應用使用Spring MVC和 Spring WebFlux,則優先MVC。測試WebFlux應用必須設定:

@SpringBootTest(properties = "spring.main.web-application-type=reactive")
public class MyWebFluxTests {

}

3. 自動裝配測試

有時候我們只需要測試框架模組整合是否正常,不需要載入整個專案。可以使用 spring-boot-test-autoconfigure 模組中一些註解。整個框架被“切片”成獨立的測試模組。

JSON 測試

測試JSON序列化與反序列化。如果是GSON或JSONB,使用 @GsonTester 或 @JsonbTester 註解。

/**
 * @author Engr-Z
 * @since 2021/1/18
 */
@JsonTest
public class MyJsonTest {

    @Autowired
    private JacksonTester<Map> json;

    @Test
    void testSerialize() throws Exception {
        Map<String, Object> map = new HashMap<>();
        map.put("name", "攻城獅·正");
        map.put("websit", "engr-z.com");
        
        Assertions.assertThat(this.json.write(map)).isEqualToJson("expected.json");
        Assertions.assertThat(this.json.write(map)).hasJsonPathStringValue("@.make");
        Assertions.assertThat(this.json.write(map)).extractingJsonPathStringValue("@.make")
                .isEqualTo("Honda");
    }

    @Test
    void testDeserialize() throws Exception {
        String content = "{\"name\":\"攻城獅·正\",\"website\":\"engr-z.com\"}";
        Assertions.assertThat(this.json.parse(content));
        Assertions.assertThat(this.json.parseObject(content).get("website")).isEqualTo("engr-z.com");
    }

}

Spring MVC 測試

測試 /demo/hello 介面是否正常

/**
 * @author Engr-Z
 * @since 2021/1/18
 */
@WebMvcTest(DemoController.class)
public class SpringMVCTest {

    @Autowired
    private MockMvc mvc;

    @Test
    void test() throws Exception {
        RequestBuilder builder = MockMvcRequestBuilders.get("/demo/hello");
        ResultActions resultActions = mvc.perform(builder);
        int status = resultActions.andReturn().getResponse().getStatus();
        Assertions.assertEquals(200, status);
    }
}

Spring WebFlux 測試

/**
 * @author Engr-Z
 * @since 2021/1/18
 */
@WebFluxTest(DemoController.class)
public class SpringWebFluxTest {

    @Autowired
    private WebTestClient webClient;

    @Test
    void test() throws Exception {
        webClient.get().uri("/demo/webflux")
                .accept(MediaType.TEXT_PLAIN)
                .exchange()
                .expectStatus().isOk();
    }
}

JDBC 測試

@JdbcTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class JdbcTransactionalTests {

}

自動裝配還支援 JPA,Redis,Rest Client 等模組測試。更多請參考:Auto-configured Tests

@MockBean 和 @SpyBean

如果一個服務依賴於遠端呼叫的結果。為了不影響我們做單元測試,可以使用@MockBean。以下是官方程式碼示例:

@SpringBootTest
class MyTests {

    @MockBean
    private RemoteService remoteService;

    @Autowired
    private Reverser reverser;

    @Test
    void exampleTest() {
        // RemoteService has been injected into the reverser bean
        BDDMockito.given(this.remoteService.someCall()).willReturn("mock");
        String reverse = reverser.reverseSomeCall();
        Assertions.assertThat(reverse).isEqualTo("kcom");
    }

}

@SpyBean@MockBean 不同之處是:對於未指定mock的方法,spy預設會呼叫真實的方法,有返回值的返回真實的返回值,而mock預設不執行,有返回值的,預設返回null


本篇只介紹 Spring Boot 整合 JUnit 單元測試,關於 JUnit 用法會在以後篇章詳細講解。


除非註明,否則均為攻城獅·正原創文章,轉載請註明出處。
本文連結:https://engr-z.com/88.html