SpringBoot单元测试(四)

222 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第25天,点击查看活动详情

本文系作者 不太自律的程序猿原创,转载请私信并在文章开头附带作者和原文地址链接。

Spring Boot测试依赖提供的测试范围

引入了spring-boot-starter-test继承了很多的测试库:

JUnit,标准的单元测试Java程序。 Spring Test和Spring Boot Test,对Spring Boot应用的单元测试。 Mockito,Java Mock测试框架,用于模拟任何Spring管理的Bean。例如在- - 单元测试中,模拟一个第三方系统接口返回的数据,而不用真正地去请求第三方接口。 AssertJ,一个assertion库,同时提供了更加多的期望值与测试返回值的比较方式。 Hamcrest,库的匹配对象。 JSONassert,对JSON对象或者JSON字符串断言的库。 JSONPath,提供向XPath那样的符号来获取JSON字段。

Spring Boot单元测试的脚手架

在使用spring.io创建的Spring Boot工程中,就默认常见了一个单元测试的类。

@RunWith(SpringRunner.class)
@SpringBootTest
public class UnitTest1 {
	@Test
    public void contextLoads(){
        
    }
}

  @RunWith是JUnit中的注解,用来通知JUnit单元测试框架不要使用内置的方式进行单元测试,向上面的写法,就是指定使用SpringRunner类来提供单元测试。   @SpringBootTest注解则是用于Spring Boot应用的测试,默认会分局报名逐级往上查找Spring Boot主程序,也就是@SpringBootApplocation注解,并在单元测试启动的时候启动该类来创建Spring上下文。所以我们在对Spring Boot应用进行单元测试的时候,在日志输出都可以看到Spring Boot应用的启动日志。

对Service层代码测试

import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.*;

@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
public class ServiceUnitTest {

    @MockBean
    private ThirdSystemService thirdSystemService;

    @Autowired
    private ISysUserService userService;

    @Test
    public void test1() {
        Long expectResult = 100L;
        given(thirdSystemService.develop()).willReturn(expectResult);
        SysUser sysUser = userService.findById(expectResult);
        System.out.println(sysUser.toString());
    }
}

@MockBean可以获取在Spring下上文管理的Bean,但是thirdSystemService这个Bean并不是真的实列,而是通过Mockito工具创建的测试实例。通过@MockBean注解模拟出来的Bean,调用方法是不会真正的调用真正的方法,适用于在依赖了第三方的系统,然而第三方的系统的对接并没有实现完成,自己可以单独测试自己的业务代码。willReturn(expectResult)说明结果永远返回100L。

测试MVC代码

  Spring Boot中还能单独测试Controller的代码,例如测试Controller中方法的参数绑定和校验之类的逻辑。可以通过@WebMvcTest注解来完成单元测试。

@RunWith(SpringRunner.class)
@WebMvcTest(SysUserController.class)
public class ServiceUnitTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void test2() throws Exception {
        MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.get("/hello/{id}", 1L);
        mockMvc.perform(requestBuilder)
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print());
    }
}

像Get方法传递参数

MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders
       .get("/hello/{id}", 1L)   // path变量
       .param("name", "hello");  // @RequestParam 获取变量。post请求也适用

文件上传

@RunWith(SpringRunner.class)
@WebMvcTest(SysUserController.class)
public class ServiceUnitTest {

   @Autowired
   private MockMvc mockMvc;

   @Test
   public void test3() throws Exception {
       // 获取文件
       FileInputStream fileInputStream = new FileInputStream("文件路径");
       // 构建文件上传对象
       MockMultipartFile mockMultipartFile = new MockMultipartFile("file", fileInputStream);
       // 构建mock文件上传请求
       MockMultipartHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.multipart("/upload").file(mockMultipartFile);
       // 发送请求
       mockMvc.perform(requestBuilder)
               .andExpect(MockMvcResultMatchers.status().isOk())
               .andDo(MockMvcResultHandlers.print());
   }
}

模拟Cookie和Session
@RunWith(SpringRunner.class)
@WebMvcTest(SysUserController.class)
public class ServiceUnitTest {

   @Autowired
   private MockMvc mockMvc;

   @Test
   public void test4() throws Exception {
       MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders
               .get("index.html")
               .sessionAttr("name", "hello")
               .cookie(new Cookie("token", "123345"));
       mockMvc.perform(requestBuilder)
               .andExpect(MockMvcResultMatchers.status().isOk())
               .andDo(MockMvcResultHandlers.print());

   }
}

设置请求头

@RunWith(SpringRunner.class)
@WebMvcTest(SysUserController.class)
public class ServiceUnitTest {

   @Autowired
   private MockMvc mockMvc;

   @Test
   public void test5() throws Exception {
       MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders
               .get("index.html")
               .content(MediaType.APPLICATION_JSON_VALUE) // 期望返回类型
               .contentType(MediaType.APPLICATION_JSON_VALUE) // 提交的内容类型
               .header("token", 1235); // 设置请求头
       mockMvc.perform(requestBuilder)
               .andExpect(MockMvcResultMatchers.status().isOk())
               .andDo(MockMvcResultHandlers.print());

   }
}

比较返回结果

  MockMvc类的perform方法会返回一个ResultAction类,可以对结果进行一些操作(andExpect、andDo和andReturn)。

@RunWith(SpringRunner.class)
@WebMvcTest(SysUserController.class)
public class ServiceUnitTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void test2() throws Exception {
        MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders
                .get("/hello/{id}", 1L)
                .param("name", "hello");

        mockMvc.perform(requestBuilder)
                .andExpect(MockMvcResultMatchers.jsonPath("$.id", "id").value(2L));
                .andDo(MockMvcResultHandlers.print());
    }
}

例如上面获取返回的JSON结果中的id字段的值,value是期望值,如果期望值与实际值不一样测试就会报错。
也可以断言测试返回结果的View(视图)和Model(数据模型)是否是期望值

@RunWith(SpringRunner.class)
@WebMvcTest(SysUserController.class)
public class ServiceUnitTest {

    @Autowired
    private MockMvc mockMvc;

    MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders
                .get("/hello/{id}", 1L)
                .param("name", "hello");

        mockMvc.perform(requestBuilder)
            	// 断言返回的试图
                .andExpect(MockMvcResultMatchers.view().name("index.html"))
            	// 断言返回的数据模型中的数据
                .andExpect(MockMvcResultMatchers.model().attribute("id",1L))
                .andDo(MockMvcResultHandlers.print());
}

更多的结果断言可以在MockMvcResultMatchers类中找到,该类是请求结果的匹配的一个工具类。

感谢诸君的观看,文中如有纰漏,欢迎在评论区来交流。如果这篇文章帮助到了你,欢迎点赞👍关注。