spring单元测试Mock,MockBean踩坑及没有真实执行的理解

Posted 郝泽龙_HZ

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring单元测试Mock,MockBean踩坑及没有真实执行的理解相关的知识,希望对你有一定的参考价值。

前言

笔者在早期就曾纠结过这个问题,当时只是简单的理解为Mock和MockBean注解的外部依赖并不会真实执行,但是这个没有真实执行又代表哪个意思呢?那Spy真实执行的理解是如何呢?Mock和MockBean又有啥区别呢?

非真实执行:

引言:一直迷惑执行的是mock的bean,但是不知道为何并没有执行又进行测试呢?
其中碰到一个实例很好:

@Test
public void mockitoTest(){
    //生成一个mock对象
    List<String> mockString = Mockito.mock(List.class);
    //打印mock对象的类名,看看mock对象为何物
    System.out.println(mockString.getClass().getName());
    //操作mock对象
    mockString.add("0");
    mockString.get(0);
    // 此时肯定如果为真实执行,那么当前应该没有数据
    mockString.clear();
    //verify验证,mock对象是否发生过某些行为
    //如果验证不通过,Mockito会抛出异常
    Mockito.verify(mockString).add("one");
    Mockito.verify(mockString).get(0);
    //指定mock方法,获取一个不存在的数据,如果真实执行,那么应该报错
    Mockito.when(mockString.get(1)).thenReturn("执行成功");
    //这里打印出“13”(但我们知道mockedList实际是没有下标为1的元素,这就是mock的功能)
    System.out.println(mockString.get(1));
}

Mock和MockBean

之前理解:二者用法几乎相同,只是初始化的时候有所不同。
现在理解:二者初始化mock方式相似,但是二者适合的场景却有很大区别

遇到问题:

笔者将MockBean改为Mock后报错:

@SpringBootTest
// 用于进行模拟的 HTTP 请求
@AutoConfigureMockMvc
public class RepeaterControllerTest extends ControllerTest {
  private final Logger logger = LoggerFactory.getLogger(this.getClass());
  @Autowired
  RepeaterController repeaterController;

  @Autowired
  protected MockMvc mockMvc;

  @Mock
  RepeaterService repeaterService;

  private static final String baseUrl = "/repeater";

  /**
   * 获得一个中继器.
   *
   * @return 中继器.
   */
  public static Repeater getOneRepeater() {
    Repeater repeater = new Repeater();
    repeater.setId(new Random().nextInt());
    repeater.setType((byte) (new Random().nextInt() % 127));
    repeater.setCreationTime(new Timestamp(System.currentTimeMillis()));
    repeater.setLastOnLineTime(new Timestamp(System.currentTimeMillis()));
    repeater.setMonitors(new ArrayList<>());
    repeater.setSpec(new net.bytebuddy.utility.RandomString().nextString());
    repeater.setPosition(new RandomString().nextString());
    repeater.setReturnValue(new Random().nextLong());
    repeater.setLatitude(BigDecimal.valueOf(Math.random()));
    repeater.setLongitude(BigDecimal.valueOf(Math.random()));
    return repeater;
  }

  @Test
  void getById() throws Exception {
    Repeater repeater = getOneRepeater();

    Mockito.doReturn(repeater).when(this.repeaterService).getById(Mockito.eq(repeater.getId()));

    String url = baseUrl + "/" + repeater.getId().toString();
    this.mockMvc.perform(MockMvcRequestBuilders.get(url))
            .andExpect(MockMvcResultMatchers.jsonPath("$.id").value(repeater.getId()))
            .andExpect(MockMvcResultMatchers.jsonPath("$.position").exists())
            .andExpect(MockMvcResultMatchers.status().isOk());
  }

mock没有生效,执行的依然是真实service:

原来也有人遇到这种问题:


1.Mock和Mockito更适合单元测试,并不能将mock的bean等装入到到整个上下文,MockBean更适合集成测试,MockBean能将mock的bean加到上下文中。
2.使用MockBean则必须要起spring环境,不利于单元测试的执行效率,使用mockito或mock的方式更好。

Spy

之前写过关于它的ppt,当时觉得理解比较困难,但是当理解了不真实执行之后,理解它可能更加轻松。
spy翻译即为“间谍”,例如上文提到的例子mockString.add("0"),在mock方法下,它并不会真正存起来,但是spy方法就真的会。当mockString.clear(),之后,我们再执行Mockito.when(mockedList.get(1)).thenReturn("执行方法");就会报错,是因为它真的执行了实际的方法,并产生影响。

总结:

本周第二次写后台登录时没有用到单元测试,而是使用了集成测试,同时在测试中运用了单元测试的方式,遇到了很多问题,但是自己查了之后对于单元测试也不再那么恐惧。

以上是关于spring单元测试Mock,MockBean踩坑及没有真实执行的理解的主要内容,如果未能解决你的问题,请参考以下文章

优雅单测-3用Mockito轻松解决复杂的依赖问题

与单元测试控制器和服务方法的区别[重复]

软件测试Junit单元测试

软件测试Junit单元测试

软件测试Junit单元测试

Spring学习12-Spring利用mock进行单元测试