Spring Boot Mock數據

背景

使用Spring Boot進行微服務或者先後端分離的相關開發設計時,一般使用接口進行對接,而此時就對項目的測試提出了相關要求,好比單元測試,繼承測試,調用第三方接口測試等等,達到相應的測試覆蓋率;三方接口測試須要本身進行模擬結果返回等,這時就須要Mock相應的數據返回達到測試效果。java

Mock數據相關

  • Mockito
  • PowerMock
  • TestNG
  • EasyMock
  • etc.

此處僅僅列舉幾個比較流行的,還有不少實用的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

1.使用Mockito(不涉及特殊方法Mock)

因爲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);
  }
}

複製代碼

2.使用PowerMock(可對特殊方法Mock)

由於跨層,當測試代碼使用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

  1. PowerMock
  2. 版本對接
相關文章
相關標籤/搜索