微服務單元測試 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掉,這樣拿到的就是正常的返回值。