Spring Boot+JUnit5+Mockito单元测试

Spring Boot+JUnit5+Mockito单元测试

导语:

最近领导要求项目添加单元测试,指定用JUnit5Mockito,之前没玩过这两个东西,这几天在网上查了很多资料,略懂皮毛,这篇文章打算把这些东西整理出来。

一,单元测试和JUnit

单元测试

要玩这个东西,首先得知道什么是单元测试,这个很重要,我之前一直纠结写单元测试的意义在哪。
单元测试就是针对最小的功能单元编写测试代码,Java程序最小的功能单元就是方法,因此,对Java程序进行单元测试就是针对单个Java方法进行测试。

先了解一下什么是测试驱动开发
所谓测试驱动开发,是指先编写接口,紧接着编写测试。编写完测试后,我们才开始真正编写实现代码。在编写实现过程中,我们一边写一边测,什么时候测试全部通过了,就表示编写的实现完成了。而当接口需要修改时,修改完只要跑一遍单元测试,如果过了,说明修改没有问题。
然而这是一种理想的情况,现实中大部分情况都是我们已经写好了实现的代码,需要对已有的代码进行测试。

在没有用JUnit之前我们通常会使用main()来运行测试代码,接口实现写完了,用main()跑一遍,但是使用main()有很多缺点,一是不能把测试代码分离,二是main()只能有一个,三是main()是静态的会影响很多测试,谁用谁知道。

所以我们需要了解一下JUnit框架。

JUnit框架

JUnit是一个开源的Java语言的单元测试框架,专门针对Java设计,使用最广泛。JUnit目前最新版本是JUnit5。
关于JUnit的使用这里就不写了,网上教程一大把,贴上个比较完整的教程JUnit教程
Maven依赖


    org.junit.platform
    junit-platform-launcher
    1.5.2
    test


    org.junit.jupiter
    junit-jupiter-engine
    5.5.2
    test


    org.junit.vintage
    junit-vintage-engine
    5.5.2
    test

二,Mockito框架

既然是对程序中的某个方法进行测试,难免会遇到方法中有外部依赖的时候,这时候就可以用Mockito来进行模拟外部依赖,比如一个controller中有service调用,这时就可以用Mockito把这个Service对象Mock掉,真正的实现"单元"测试。
关于Mockito使用,这里也贴上教程Mockito教程

三,Controller层测试示例

了解完这两个东西就可以使用他们来测试代码了,首先看下Controller层,在Controller层中我们的每个方法都是一个HTTP请求的入口,所以我们要测试Controller代码需要模拟HTTP请求,使用MockMvc就可以做到。

MockMvc的使用方式:

  1. MockMvcBuilder构造MockMvc的构造器
  2. MockMvcRequestBuilders创建请求request
  3. mockMvc调用perform,执行一个request请求,调用controller的业务处理逻辑,返回ResultActions
  4. 可以通过ResultActions, MockMvcResultMatchers对结果进行验证

示例一(GET请求不带参数):

@SpringBootTest()
@Slf4j
public class MockMvcDemo extends AbstractBaseTest {
    private MockMvc mvc;
     
    @BeforeEach
    public void setUp() {
        // (1)构建mvc环境
        mvc = MockMvcBuilders.standaloneSetup(new AccountController()).build();
    }
    
    @Test
    public void test() throws Exception {
        // (2)构建请求
        MockHttpServletRequestBuilder request = MockMvcRequestBuilders.get("/account/info")
                .contentType("text/html")
                .accept(MediaType.APPLICATION_JSON);
  
        // (3)发送请求,获取请求结果
        ResultActions perform = mvc.perform(request);
  
        // (4)请求结果校验
        perform.andExpect(MockMvcResultMatchers.status().isOk());
  
        MvcResult mvcResult = perform.andReturn();
        MockHttpServletResponse response = mvcResult.getResponse();
  
        // (5)校验返回信息
         log.info(response.getContentAsString());
    }
}  

示例二(POST请求带token和参数对象):

如果是POST请求带参数对象,可以使用MockMvcRequestBuilders.post方法模拟POST请求

@SpringBootTest()
@Slf4j
public class MockMvcDemo extends AbstractBaseTest {
    private MockMvc mvc;
     
    @BeforeEach
    public void setUp() {
        // (1)构建mvc环境
        mvc = MockMvcBuilders.standaloneSetup(new AccountController()).build();
    }
    
    @Test
    public void test() throws Exception {
        // (2)构建请求
        MockHttpServletRequestBuilder request = MockMvcRequestBuilders.post("/account/info")
                .header("Authorization",token)
                .contentType(MediaType.APPLICATION_JSON)
                .content(JSON.toJSONString(params));
  
        // (3)发送请求,获取请求结果
        ResultActions perform = mvc.perform(request);
  
        // (4)请求结果校验
        perform.andExpect(MockMvcResultMatchers.status().isOk());
  
        MvcResult mvcResult = perform.andReturn();
        MockHttpServletResponse response = mvcResult.getResponse();
  
        // (5)校验返回信息
         log.info(response.getContentAsString());
    }
} 

示例三(使用Mockito模拟掉外部依赖):

Controller中一般都会依赖其他Service服务,可以使用Mockito模拟掉,只专注于Controller层的测试,假设AccountController依赖于AccountService中的service方法,参考以下示例:

@SpringBootTest()
@Slf4j
public class MockMvcDemo extends AbstractBaseTest {
    private MockMvc mvc;

    @Mock 
    private AccountService service;

    @InjectMocks
    private AccountController controller;
     
    @BeforeEach
    public void setUp() {
         MockitoAnnotations.initMocks(this);//这句话执行以后,service自动注入到controller中。
        
        // (1)构建mvc环境
        mvc = MockMvcBuilders.standaloneSetup(controller).build();
    }
    
    @Test
    public void test() throws Exception {

        // (2)模拟外部依赖返回结果
        when(service.service(Mockito.any(Param.class))).thenReturn("success");
        
        // (3)构建请求
        MockHttpServletRequestBuilder request = MockMvcRequestBuilders.post("/account/info")
                .header("Authorization",token)
                .contentType(MediaType.APPLICATION_JSON)
                .content(JSON.toJSONString(params));
  
        // (4)发送请求,获取请求结果
        ResultActions perform = mvc.perform(request);
  
        // (5)请求结果校验
        perform.andExpect(MockMvcResultMatchers.status().isOk());
  
        MvcResult mvcResult = perform.andReturn();
        MockHttpServletResponse response = mvcResult.getResponse();
  
        // (6)校验返回信息
         log.info(response.getContentAsString());
    }
}   

注意:
有些依赖没法用@InjectMocks来自动注入,可以通过引入ReflectionTestUtils,解决依赖注入的问题。

ReflectionTestUtils.setField(controller,"service",service);

参考文章

  • 廖雪峰的官方网站-编写JUnit教程
  • SpringMVC单元测试-MockMvc

你可能感兴趣的:(Spring Boot+JUnit5+Mockito单元测试)