Java Spring Boot实战练习之单元测试篇

 更新时间:2021年10月14日 11:02:53   作者:程序猿-小菜  
单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。对于单元测试中单元的含义,一般来说,要根据实际情况去判定其具体含义,如C语言中单元指一个函数,Java里单元指一个类,图形化的软件中可以指一个窗口或一个菜单等

   在这里插入图片描述

一、关于JUnit的一些东西

  在我们开发Web应用时,经常会直接去观察结果进行测试。虽然也是一种方式,但是并不严谨。作为开发者编写测试代码来测试自己所写的业务逻辑是,以提高代码的质量、降低错误方法的概率以及进行性能测试等。经常作为开发这写的最多就是单元测试。引入spring-boot-starter-testSpringBoot的测试依赖。该依赖会引入JUnit的测试包,也是我们用的做多的单元测试包。而Spring Boot在此基础上做了很多增强,支持很多方面的测试,例如JPA,MongoDB,Spring MVC(REST)和Redis等。
接下来主要是测试业务逻辑层的代码,REST和Mock测试。

1.1 JUnit介绍

  JUnit是一个Java语言的单元测试框架。它由Kent Beck和Erich Gamma建立,逐渐成为源于Kent Beck的sUnit的xUnit家族中最为成功的一个。 JUnit有它自己的JUnit扩展生态圈。多数Java的开发环境都已经集成了JUnit作为单元测试的工具。

JUnit相关概念 含义
测试 一个以@Test注释的方法定义一个测试,运行这个方法,JUnit会创建一个包含类的实例,然后再调用这个被注释的方法。
测试类 包含多个@Test方法的一个类
Assert 定义想测试的条件,当条件成立时,assert 方法保持沉默,条件不成立时,则抛出异常
Suite Suite允许将测试类归类成一组
Runner Runner类用于运行测试,JUnit4是向后兼容的,可以运行JUnit3的测试实例

  这里使用的是JUnit4.x版本,JUnit中有两个重要的类Assume+Assert,以及重要的注解:BeforeClass、AfterClass、After、Before、Test和Ignore。BeforeClass和AfterClass在每个类的开始和结束的时候运行,需要static修饰方法。而Before和After则是在每个测试方法的开始和结束的时候运行。
代码片段:TestDeployApplication.class是自己编写的Spring Boot启动类。

@RunWith(SpringRunner.class)
@SpringBootTest(classes = {TestDeployApplication.class})
public class UnitTest1 {

    @BeforeClass
    public static void beforeClass() {
        System.out.println("=================BeforeClass================");
    }

    @AfterClass
    public static void afterClass() {
        System.out.println("=================AfterClass================");
    }

    @Before
    public void beforeTest() {
        System.out.println("before test");
    }

    @After
    public void afterTest() {
        System.out.println("after test");
    }

    @Test
    public void test1() {
        System.out.println("test1");
    }

    @Test
    public void test2() {
        System.out.println("test2");
    }
}

1.2 JUnit的Assert类

Assert类中常用的方法:

  • assertEquals(“提示信息”,A,B):当判断A是否等于B,不等于就抛出错误。比较对象是调用的是equals()方法。
  • assertSame(“提示信息”,A,B):判断对象是否相同。
  • assertTrue(“提示信息”,A):判断条件A是否为真。
  • assertFalse(“提示信息”,A):判断条件是否为假。
  • assertNotNull(“提示信息”,A):判断对象是否不为空。
  • assertNull(“提示信息”,A):判断对象是否不为空。
  • assertArrayEqual(“提示信息”,A,B):判断数组A和数组B是否相等。

1.3 JUnit的Suite

  JUnit的Suite设计就是一次性运行一个或多个测试用例,Suite可以看作是一个容器,用来把测试类归类在一起,并把他们作为一个集合来运行,运行器启动Suite。

@RunWith(Suite.class)
@SuiteClasses({UnitTest1.class,UnitTest2.class})
public class MainTest{
    
}

二、Spring Boot单元测试

添加需要的依赖

<dependency>
	<groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

2.1 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字段。

2.2 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应用的启动日志。

2.3 对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。

2.4 测试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());

   }
}

2.5 比较返回结果

  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类中找到,该类是请求结果的匹配的一个工具类。

  如果对软件测试、接口测试、自动化测试、持续集成、面试经验。感兴趣  可以进到806549072,群内会有不定期的分享测试资料。还会有技术大牛,业内同行一起交流技术

到此这篇关于Java Spring Boot实战练习之单元测试篇的文章就介绍到这了,更多相关Java Spring Boot 单元测试内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java compiler没有1.8怎么解决

    java compiler没有1.8怎么解决

    这篇文章主要介绍了java compiler没有1.8的解决方法,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-08-08
  • IDEA 自定义方法注解模板的实现方法

    IDEA 自定义方法注解模板的实现方法

    这篇文章主要介绍了IDEA 自定义方法注解模板的实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-09-09
  • ClassLoader双亲委派模式作用详解

    ClassLoader双亲委派模式作用详解

    这篇文章主要为大家介绍了ClassLoader双亲委派模式作用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-10-10
  • Spring boot 整合RabbitMQ实现通过RabbitMQ进行项目的连接

    Spring boot 整合RabbitMQ实现通过RabbitMQ进行项目的连接

    RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种客户端,这篇文章主要介绍了Spring boot 整合RabbitMQ实现通过RabbitMQ进行项目的连接,需要的朋友可以参考下
    2022-10-10
  • 分析并发编程之LongAdder原理

    分析并发编程之LongAdder原理

    LongAdder类是JDK1.8新增的一个原子性操作类。AtomicLong通过CAS算法提供了非阻塞的原子性操作,相比受用阻塞算法的同步器来说性能已经很好了,但是JDK开发组并不满足于此,因为非常搞并发的请求下AtomicLong的性能是不能让人接受的
    2021-06-06
  • springboot图片验证码功能模块

    springboot图片验证码功能模块

    用户登录几乎是一个线上系统必不可少且使用相对比较频繁的一个模块,为了防止恶意暴力尝试,防止洪水攻击、防止脚本自动提交等,验证码是一个较为便捷且行之有效的预防手段,这篇文章主要介绍了springboot图片验证码功能模块,需要的朋友可以参考下
    2022-04-04
  • SpringBoot使用Micrometer实现度量和监控

    SpringBoot使用Micrometer实现度量和监控

    在构建和维护现代应用程序时,度量和监控是至关重要的,它们可以帮助您了解应用程序的性能、稳定性和可用性,本文将介绍如何在Spring Boot应用程序中使用Micrometer进行度量和监控,需要的朋友可以参考下
    2023-10-10
  • Java JVM类加载机制解读

    Java JVM类加载机制解读

    JVM将class文件字节码文件加载到内存中, 并将这些静态数据转换成方法区中的运行时数据结构,在堆(并不一定在堆中,HotSpot在方法区中)中生成一个代表这个类的java.lang.Class 对象,作为方法区类数据的访问入口,接下来将详细讲解JVM类加载机制
    2021-11-11
  • java char数据类型原理解析

    java char数据类型原理解析

    这篇文章主要介绍了java char数据类型原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-02-02
  • 解读Java和JavaScript区别与联系

    解读Java和JavaScript区别与联系

    这篇文章主要介绍了解读Java和JavaScript区别与联系,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02

最新评论