springboot 单元测试实践2

301 阅读2分钟

本文重点将讲述如何在单元测试中, mock 某个service的请求。

测试环境中,我们经常会有这样的场景,不希望在服务A执行单测的过程中调用某个服务B,比如说服务B根本没有测试环境,或者服务B测试环境不一定能跑的通。

举个简单的例子,我们在服务A中调用服务B的请求来发送钉钉消息,单测中执行的时候,是不想真正触发发送钉钉消息到钉钉群里。

当然,这种情况也有其他方式来玩,比如单独建个钉钉测试群,来接收单测环境的信息,或者干脆就配置一个错误的钉钉token。

抛开上面的方式不谈,我们今天就讲讲如何在单测环境中mock发送钉钉消息的服务。

还是之前的那个mockito-demo的工程,我们在工程中新增了两个service: DemoService,DingTalkService,两个service服务的代码如下:

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Service
@AllArgsConstructor
@Slf4j
public class DemoService {

  private final DingTalkService dingTalkService;

  public void service() {
    log.info("use DemoService call service function!");
    dingTalkService.send2DingDing("hello world");
  }
}
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class DingTalkService {

  public void send2DingDing(String content) {
    log.info("send content to dingding success!");
  }
}

DemoService中对外提供了service的方法,调用DemoServcie提供的service的方法,首先会控制台打印一条日志,然后调用发送钉钉消息的服务DingTalkService提供的send2Dingding的方法来执行发送消息到钉钉群,调用DingTalkService提供的send2Dingding的方法,为了模拟效果,我们也打印了一条send content to dingding success的日志,用这个日志来代表send2Dingding的方法实际上是执行了。

接下来,我们建立一个单元测试类 DemoServiceTest,还是老样子,我们在打开的DemoService中,使用快捷键的方式来创建单元测试类,mac下是 cmd + shift + T,Windows下是ctrl + shift + T,DemoServiceTest的代码如下:

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doNothing;

import com.example.demo.extensions.CaptureSystemOutput;
import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import lombok.SneakyThrows;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;

@SpringBootTest
@CaptureSystemOutput
class DemoServiceTest {

  @Resource DemoService demoService;

  @MockBean DingTalkService dingTalkService;

  @SneakyThrows
  @BeforeEach
  void initMock() {
    doNothing().when(dingTalkService).send2DingDing(anyString());
  }

  @Test
  void service(@NotNull CaptureSystemOutput.OutputCapture outputCapture) {
    demoService.service();
    assertTrue(outputCapture.toString().contains("use DemoService call service function"));
    assertFalse(outputCapture.toString().contains("send content to dingding success"));
  }
}

复制完代码,记得执行下 ./gradlew spotlessApply 格式化下代码

单测中,通过MockBean的方式,把DingTalkService给mock掉了,在调用DingTalkService的send2Dingding的方法时,什么都不做。

这样,打印的日志中,也就只有DemoService的service方法的日志,而没有DingTalkService的send2Dingding方法执行的内部日志,说明DingTalkService我们已经mock成功了。