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. 版本对接
相关文章
相关标签/搜索