9

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

 3 years ago
source link: https://segmentfault.com/a/1190000040553331
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

笔者在早期就曾纠结过这个问题,当时只是简单的理解为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:

image.png

原来也有人遇到这种问题

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

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

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK