1. 程式人生 > 程式設計 >微服務單元測試 Mock使用與詳解

微服務單元測試 Mock使用與詳解

最近在搞微服務的專案,搞完後發現內部需要呼叫別的服務的介面,可是另一個服務還沒有寫完我還調不通,哪這就非常尷尬了。這種情況下要怎麼測試呢?這時就需要引入Mock的概念。

1 什麼是Mock

mock是在測試過程中,對於一些不容易構造/獲取的物件,建立一個mock物件來模擬物件的行為。比如說你需要呼叫B服務,可是B服務還沒有開發完成,那麼你就可以將呼叫B服務的那部分給Mock掉,並編寫你想要的返回結果。

2 Spring Boot的測試類庫

現在絕大多數的java服務都是Spring框架搭建的,並且也會使用到Spring boot來進行快速搭建開發,在Spring Boot提供了許多實用工具和註解來幫助測試應用程式,主要包括以下兩個模組:

  • spring-boot-test:支援測試的核心內容。
  • spring-boot-test-autoconfigure:支援測試的自動化配置。

開發進行只要使用 spring-boot-starter-test 啟動器就能引入這些 Spring Boot 測試模組,還能引入一些像 JUnit,AssertJ,Hamcrest 及其他一些有用的類庫,具體如下所示:

  • JUnit:Java 應用程式單元測試標準類庫。
  • Spring Test & Spring Boot Test:Spring Boot 應用程式功能整合化測試支援。
  • AssertJ:一個輕量級的斷言類庫。
  • Mockito:一個Java Mock測試框架,預設支付 1.x,可以修改為 2.x。
  • JsonPath:一個JSON操作類庫。

3 編寫測試用例

1 引入pom依賴

再IDEA中建立一個普通的maven專案即可,然後匯入pom依賴:

<parent>
        <groupId> org.springframework.boot </groupId>
        <artifactId> spring-boot-starter-parent </artifactId>
        <version>2.1.7.RELEASE </version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test
</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.20</version> </dependency> </dependencies> 複製程式碼

3.2 MockMVC基於RESTful風格的測試

對於前後端分離的專案而言,無法直接從前端靜態程式碼中測試介面的正確性,因此可以通過MockMVC來模擬HTTP請求。基於RESTful風格的SpringMVC的測試,我們可以測試完整的Spring MVC流程,即從URL請求到控制器處理,再到檢視渲染都可以測試。

首先建立一個超簡單的controller

@RestController
@RequestMapping(value = "/web")
public class WebController {

    @PostMapping(value = "/create")
    public WebResponse<String> ping(@RequestBody WebRequest webRequest){
        System.out.println(webRequest);
        WebResponse<String> response = new WebResponse<>();
        response.setBody("create 完成---");
        response.setCode("00000");
        response.setMessage("成功");
        return response;
    }
}
複製程式碼

request和response

@Data
@ToString
@EqualsAndHashCode
public class WebRequest {
    private String name;
    
    private String mobile;
}
複製程式碼
@Data
@ToString
@EqualsAndHashCode
public class WebResponse<T> {
    private String code;

    private String message;

    private T body;
}
複製程式碼

然後建立一個測試用例類

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class WebControllerIT {

    @Autowired
    private WebApplicationContext mac;

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void ping() throws Exception {
        //請求的json
        String json = "{\"name\":\"王五\",\"mobile\":\"12345678901\"}";

        //perform,執行一個RequestBuilders請求,會自動執行SpringMVC的流程並對映到相應的控制器執行處理
        mockMvc.perform(MockMvcRequestBuilders
                //構造一個post請求
                .post("/web/create")
                //json型別
                .contentType(MediaType.APPLICATION_JSON_UTF8)
                //使用writeValueAsString()方法來獲取物件的JSON字串表示
                .content(json))
                //andExpect,新增ResultMathcers驗證規則,驗證控制器執行完成後結果是否正確,【這是一個斷言】
                .andExpect(MockMvcResultMatchers.status().is(200))
                .andExpect(MockMvcResultMatchers.status().isOk())
                //使用jsonPaht驗證返回的json中code欄位的返回值
                .andExpect(MockMvcResultMatchers.jsonPath("$.code").value("00000"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.message").value("成功"))
                //body屬性不為空
                .andExpect(MockMvcResultMatchers.jsonPath("$.body").isNotEmpty())
                //新增ResultHandler結果處理器,比如除錯時 列印結果(print方法)到控制檯
                .andDo(MockMvcResultHandlers.print())
                //返回相應的MvcResult
                .andReturn();
    }

}
複製程式碼

其中MockMvcRequestBuilders 寫好後直接執行就可以了,從控制檯就可以看到詳細資訊。

4 Mockito

4.1 Mockito是什麼

Mockito是mocking框架,它讓你用簡潔的API做測試。而且Mockito簡單易學,它可讀性強和驗證語法簡潔。 Mockito是GitHub上使用最廣泛的Mock框架,並與JUnit結合使用.Mockito框架可以建立和配置mock物件.使用Mockito簡化了具有外部依賴的類的測試開發!

4.2 使用Mockito

加入一個service

public interface WebService {

    String web(String string);
}
複製程式碼
@Service
public class WebServiceImpl implements WebService {

    @Override
    public String web(String string) {
        return "WebServiceImpl 執行成功";
    }
}
複製程式碼

修改controller

@RestController
@RequestMapping(value = "/web")
public class WebController {

    @Autowired
    private WebService webService;

    @PostMapping(value = "/create")
    public WebResponse<String> ping(@RequestBody WebRequest webRequest){

        //呼叫service
        String str = webService.web(webRequest.getMobile());

        WebResponse<String> response = new WebResponse<>();
        response.setBody(str);
        response.setCode("00000");
        response.setMessage("成功");
        return response;
    }
}
複製程式碼

修改測試用例

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class WebControllerIT {

    @Autowired
    private WebApplicationContext mac;

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private WebService webService;

    @Test
    public void ping() throws Exception {

        doReturn("Mockito WebServiceImpl 執行完成").when(webService).web(anyString());

        //請求的json
        String json = "{\"name\":\"王五\",執行一個RequestBuilders請求,會自動執行SpringMVC的流程並對映到相應的控制器執行處理
        mockMvc.perform(MockMvcRequestBuilders
                //構造一個post請求
                .post("/web/create")
                //json型別
                .contentType(MediaType.APPLICATION_JSON_UTF8)
                //使用writeValueAsString()方法來獲取物件的JSON字串表示
                .content(json))
                //andExpect,新增ResultMathcers驗證規則,驗證控制器執行完成後結果是否正確,【這是一個斷言】
                .andExpect(MockMvcResultMatchers.status().is(200))
                .andExpect(MockMvcResultMatchers.status().isOk())
                //使用jsonPaht驗證返回的json中code欄位的返回值
                .andExpect(MockMvcResultMatchers.jsonPath("$.code").value("00000"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.message").value("成功"))
                //body屬性不為空
                .andExpect(MockMvcResultMatchers.jsonPath("$.body").isNotEmpty())
                //新增ResultHandler結果處理器,比如除錯時 列印結果(print方法)到控制檯
                .andDo(MockMvcResultHandlers.print())
                //返回相應的MvcResult
                .andReturn();
    }

}
複製程式碼

輸出結果:

從上面的程式碼可以看到,我們新增了一個webService,並增加了@MockBean註解,表示將webService給mock調,這樣我們就可以增加自己想要得webService返回結果。 在測試用例中我們增加了doReturn()方法,這段程式碼的含義是當呼叫WebService中的web()方法時(anyString()表示傳入web()方法中的引數是任意的String型別,當然還有個anyInt()等方法),返回Mockito WebServiceImpl 執行完成。當然你也可以不將WebService給mock掉,這樣拿到的就是正常的返回值。