使用Spring Boot進行微服務或者先後端分離的相關開發設計時,一般使用接口進行對接,而此時就對項目的測試提出了相關要求,好比單元測試,繼承測試,調用第三方接口測試等等,達到相應的測試覆蓋率;三方接口測試須要本身進行模擬結果返回等,這時就須要Mock相應的數據返回達到測試效果。java
此處僅僅列舉幾個比較流行的,還有不少實用的Mock框架,分別有不一樣的特色,如PowerMock能夠實現靜態方法,私有方法等方法的mock;相關的mock框架你們能夠自行查找,這裏給出PowerMock連接(GitHub).git
在項目中,若是不須要對靜態方法,私有方法等特殊進行驗證測試,則僅僅使用Spring boot自帶的Mockito便可完成相關的測試數據Mock,若須要則可使用PowerMock,簡單實用,結合Spring可使用註解注入;github
一些簡單的使用這裏再也不贅述,能夠查詢相應的文章(基礎文章不少),這裏主要講述的是項目中遇到的須要mock調用的第三方接口,在測試Controller層時須要跨層mock封裝好的第三方接口返回數據,驗證程序是否正確。(如圖所示,mock數據跨層)web
網上查找不少文章,大部分是重複且無用的基礎使用,並沒有在項目中進行跨層mock數據;跨層mock因涉及到Spring的IOC容器自動注入,因此基礎教程的mock不涉及跨層mock,通過實踐,總結出已經在項目中使用的Mock操做;spring
因爲Mockito集成到Sring Boot的Junit測試之中,使用時不須要特別引入,直接使用便可:(代碼以下)json
public abstract class AbstractMockBeanTest {
/** * BasiceServer封裝了對應的三方接口服務,主要是轉換接口返回結果數據 * 使用MockBean註解,自動將IOC容器中須要的對象直接替換成Mock對象,而後Mock相應數據 */
@MockBean
protected BasicServer basicServer;
/** * 如需 basicServer對應的方法須要返回什麼類型結果,請在測試前自行調用如下對應方法 */
protected void mockGetCarTypeSuccess() {
String msg = "{\"code\":200,\"data\":{\"businessType\":0,\"capacityType\":0,\"engineType\":0,\"id\":0," +
"\"seats\":0},\"msg\":\"success\",\"ok\":true}";
Mockito.when(basicServer.getCarType(anyLong())).thenReturn(msg);
}
protected void mockGetCarTypeSuccessNoData() {
String msg = "{\"code\":200,\"msg\":\"success\",\"ok\":true}";
Mockito.when(basicServer.getCarType(anyLong())).thenReturn(msg);
}
protected void mockGetCitySuccess() {
String msg = "{\"code\":200,\"data\":{\"businessType\":0,\"capacityType\":0,\"engineType\":0,\"id\":0," +
"\"seats\":0},\"msg\":\"success\",\"ok\":true}";
Mockito.when(basicServer.getCityByCode(anyString())).thenReturn(msg);
}
protected void MockGetCitySuccessSuccessNoData() {
String msg = "{\"code\":200,\"msg\":\"success\",\"ok\":true}";
Mockito.when(basicServer.getCityByCode(anyString())).thenReturn(msg);
}
}
複製代碼
由於跨層,當測試代碼使用IOC容器自動注入相應對象時,須要手動指定將自動注入的對象替換爲本身Mock的對象,才能生效;替換過程使用反射指定。(代碼以下)後端
package cn.skio.capacity.apis.v1;
import cn.skio.capacity.application.impl.CarServiceImpl;
import cn.skio.capacity.third.basic.v1.BasicService;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.hamcrest.core.Is;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.modules.junit4.PowerMockRunnerDelegate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.WebApplicationContext;
import java.util.Optional;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.handler;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/** * @author Jasmine * @date 19/06/19 */
@ActiveProfiles("test")
@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(SpringRunner.class)
@PrepareForTest({BasicService.class})
@PowerMockIgnore({"javax.management.*", "javax.net.ssl.*"})
@SpringBootTest
public class TestController {
@Autowired
private WebApplicationContext context;
private MockMvc mvc;
@Mock
private BasicService basicService;
@Autowired
private CarServiceImpl carService;
@Before
public void setUp() throws Exception {
mvc = MockMvcBuilders.webAppContextSetup(context).build();
// 使用反射將IOC容器注入的service的屬性替換指定爲mock的對象
ReflectionTestUtils.setField(carService, "basicService", basicService);
PowerMockito.when(this.basicService, "getCarType", anyLong()).thenReturn(Optional.empty());
}
@Test
@Transactional
public void testA() throws Exception {
MvcResult result = mvc.perform(MockMvcRequestBuilders.get("/v1/cars")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.accept(MediaType.APPLICATION_JSON)
).andExpect(status().isOk())
.andExpect(handler().handlerType(CarController.class))
.andExpect(handler().methodName("search"))
.andDo(MockMvcResultHandlers.print())
.andReturn();
JSONObject object = JSON.parseObject(result.getResponse().getContentAsString(), JSONObject.class);
Assert.assertThat("數據量不是2", object.getJSONObject("data").getJSONArray("list").size(), Is.is(2));
}
}
複製代碼
PowerMock相比Mockito提供更多的Mock方法,使用相對靈活,推薦使用。api
使用PowerMock如今對應的gradle依賴爲,使用powermock-api-mockito2,不要使用powermock-api-mockito:mvc
testCompile group: 'org.powermock', name: 'powermock-module-junit4', version: '2.0.2'
testCompile group: 'org.powermock', name: 'powermock-api-mockito2', version: '2.0.2'
testCompile group: 'org.mockito', name: 'mockito-core', version: '2.28.2'
複製代碼
詳細的能夠參考如下連接:app