2025/07/28

Spring Boot 3 unit test

libary 有兩個

  • spring-boot-test: 測試核心功能

  • spring-boot-test-autoconfigure: 測試的自動設定

spring-boot-starter-test 是集合

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

測試還包含其他 library

  • JUnit 5

  • AssertJ

  • Hamcrest

  • Mockito

  • JSONassert

  • JsonPath


spring 提供一個 @SpringBootTest annotation,裡面有一個 webEnvironment 環境變數,支援以下設定

  • MOCK (default)

    載入一個 WebApplicationContext 並提供 Mock Web Environment。不會啟動內嵌的 web server,並可以結合 @AutoConfigureMockMvcor @AutoConfigureWebTestClient 進行 MOCK 測試

  • RANDOM_PORT

    載入一個 WebApplicationContext,提供一個真實的 web environment,隨機 port

  • DEFINED_PORT

    載入一個 WebApplicationContext,提供一個真實的 web environment,指定 port,預設 8080

  • NONE

    載入一個 WebApplicationContext,不提供 web environment


Spring MVC 測試

MvcTests.java

package com.test.test;

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 java.util.HashMap;
import java.util.Map;

import static org.assertj.core.api.Assertions.assertThat;

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

    @Test
    public void getUserTest(@Autowired TestRestTemplate testRestTemplate) {
        Map<String, String> multiValueMap = new HashMap<>();
        multiValueMap.put("username", "test1");
        Result result = testRestTemplate.getForObject("/user/get?username={username}",
                Result.class, multiValueMap);
        assertThat(result.getCode()).isEqualTo(0);
        assertThat(result.getMsg()).isEqualTo("ok");
    }

}

呼叫 /user/test,檢查回傳的資料是否正確

從 log 可發現 tomcat 運作在另一個 port

Tomcat started on port 53594 (http) with context path '/'

MOCK 測試

package com.test.test;

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 static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
@AutoConfigureMockMvc
class MockMvcTests {

    @Test
    public void getUserTest(@Autowired MockMvc mvc) throws Exception {
        mvc.perform(MockMvcRequestBuilders.get("/user/get?username={username}", "test"))
                .andExpect(status().isOk())
                .andExpect(content().string("{\"code\":0,\"msg\":\"ok\",\"data\":\"test\"}"));
    }

}

在class 上面增加 @AutoConfigureMockMvc ,然後在 method 上加上 @Autowired MockMvc mvc 就可進行 MOCK 測試。

從 log 發現並沒有真的啟動一個 web server


MOCK 元件測試

package com.test.test;

import com.test.test.service.UserService;
import org.junit.jupiter.api.Test;
import org.mockito.BDDMockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.bean.override.mockito.MockitoBean;

import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest
class MockBeanTests {

//    @Autowired
//    private UserService userService;

    @MockitoBean
    private UserService userService;

    @Test
    public void countAllUsers() {
        BDDMockito.given(this.userService.countAllUsers()).willReturn(88);
        assertThat(this.userService.countAllUsers()).isEqualTo(88);
    }

}

@MockitoBean 代表該變數是被 Mock 覆蓋

這邊透過 BDDMockito 模擬 countAllUsers 回傳結果


JSON 測試

spring 提供各種技術元件的單元測試

想要測試 JSON,可使用 JsonTestersAutoConfiguration,只需要在測試類別加上 @JsonTest

先新增一個檔案 test/resources/jack.json

{"id":10001, "name":"Jack", "birthday": "2000-10-08 21:00:00"}

User POJO

package com.test.test;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;

import java.time.LocalDateTime;

@Data
@AllArgsConstructor
public class User {
    private long id;
    private String name;
    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime birthday;

}

JsonTests.java

package com.test.test;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.json.JsonTest;
import org.springframework.boot.test.json.JacksonTester;

import java.time.LocalDateTime;

import static org.assertj.core.api.Assertions.assertThat;

@JsonTest
class JsonTests {

    @Autowired
    private JacksonTester<User> json;

    @Test
    void serialize() throws Exception {
        User user = new User(10001L, "Jack",
                LocalDateTime.of(2000, 10, 8, 21, 0, 0));
        assertThat(this.json.write(user)).isEqualToJson("/jack.json");
        assertThat(this.json.write(user)).hasJsonPathStringValue("@.name");
        assertThat(this.json.write(user)).
                extractingJsonPathStringValue("@.name").isEqualTo("Jack");
    }

    @Test
    void deserialize() throws Exception {
        String content = "{\"id\":10002, \"name\":\"Petty\", \"birthday\": \"2025-01-01 01:01:00\"}";
        assertThat(this.json.parse(content))
                .isEqualTo(new User(10002L, "Petty",
                        LocalDateTime.of(2025, 1, 1, 1, 1, 0)));
        assertThat(this.json.parseObject(content).getName()).isEqualTo("Petty");
    }

}

沒有留言:

張貼留言