SpringBoot 测试实践 - 1:常用的工具
我自己接触到的一些商业或是开源的基于 SpringBoot 项目,它们大部分是没有测试代码的,test 文件夹只有脚手架初始化生成的那个测试类,跟不同的开发聊到这个话题,发现他们中的大部分没有写测试的习惯,或者是觉得写测试代码麻烦,主要还是依赖测试工程师做黑盒的测试。只做黑盒测试的话有一定的的局限性,一些比较***钻的案例可能就覆盖不到,而且相对来说人也比较容易出错、遗漏。而测试代码能解决其中很大一部分的问题,利用好单元测试和集成测试在某些情况下相对于直接通过 UI 进行测试是要更方便、节省时间的,所以想通过几篇博客来分享一下自己的测试实践
为什么要写测试(优点)
- 覆盖更多的边界条件,且随时都可以运行测试代码(一劳永逸)
 - 缩小测试范围:测试某个方法只需要运行对应的测试代码,而不需要运行整个项目通过请求接口进行测试
 - 对重构更友好,可以随时重构有集成测试的代码,不用担心打破原有的代码
 - 其他人也可以通过测试快速地理清楚对应被测代码的主线逻辑(类似文档的作用,特别是复杂代码,通过测试能快速理解上手)
 - 写测试的过程,给自己一个新的视角去审视代码结构的设计,有助于改善代码设计
 
当然代码方式的测试也并非完美无缺:测试代码增加编写和维护的成本,同时一些外部依赖也需要通过 Mock 的方式实现,这些都提高了整个测试编写的门槛。也倒逼我们思考更好地组织代码,减少依赖
另一个方面:测试对于重构也是至关重要的,随着对业务的理解越来越深刻,可以重构代码,抽象出了一些共性的逻辑,优化代码结构,但是如果没有相关测试,面对着旧代码就只能望而却步了
测试工具:JUnit 5, AssertJ,Mockito
spring-boot-starter-test 自带常用的测试工具:JUnit5、Assertj、Mockito,可以直接使用
JUnit5
Junit 5 包含:
- JUnit Platform:Test Engine
 - Jupiter:编程模型和拓展模型
 - Vintage:兼容老版本
 
JUnit 4 和 5 使用的包有所不同
// JUnit 4
import org.junit.Test;
import static org.junit.Assert.assertEquals;
// JUnit 5
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
如果不考虑兼容 JUnit 4 的测试,我们可以直接在依赖中直接排除 JUnit 4 的依赖,这样也可以避免在使用的时候错误地引入 JUnit 4 的包
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <version>{spring-boot-starter-test-version}</version>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
            </exclusion>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>
还有一点值得注意的是:JUnit 5 中 @RunWith 被 @ExtendWith 代替
AssertJ
AssertJ 是一种支持链式编程的断言库,相对于 JUnit 自带的断言,它提供了更多的方法,也提供了更好的断言不匹配时的信息展示
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class MyTest {
    // 变量断言
    @Test
    public void test() {
        String name = "Alice";
        int age = 30;
        assertThat(name).isEqualTo("Alice");
        assertThat(age).isGreaterThan(18).isLessThan(60);
    }
    // List 断言
    @Test
    public void testList() {
        List<String> list = Arrays.asList("foo", "bar", "baz")
        assertThat(list).containsExactly("foo", "bar", "baz").hasSize(3);
    }
    // Map 断言
    @Test
    public void testMap() {
        Map<String, Integer> map = new HashMap<>();
        map.put("apple", 1);
        map.put("banana", 2);
        map.put("orange", 3);
        assertThat(map).containsEntry("banana", 2);
        assertThat(map).containsKey("banana");
        assertThat(map).containsValue(2);
    }
    // hasNoNullFieldsOrProperties 来断言测试对象的每个属性都不为 null
    @Test
    public void testHasNoNullFieldsOrProperties() {
        Person person = new Person("Alice", 30);
        assertThat(person).hasNoNullFieldsOrProperties();
    }
    // 异常断言
    @Test
    public void testDivideByZeroThrowsException() {
        assertThatThrownBy(() -> {
            int result = 1 / 0;
        }).isInstanceOf(ArithmeticException.class)
          .hasMessageContaining("/ by zero");
    }
}
Mockito
Mockito 是一个 Java Mock 框架,用于创建各种类型的 Mock 对象,并设置 Mock 对象的行为
import static org.mockito.Mockito.*;
import org.junit.jupiter.api.Test;
public class MyTest {
    // 创建 mock 对象
    @Test
    public void testCreateMock() {
        List<String> list = mock(List.class);
    }
    // 设置 mock 对象的行为
    @Test
    public void testMockBehavior() {
        List<String> list = mock(List.class);
        when(list.get(0)).thenReturn("foo");
        when(list.size()).thenReturn(1);
    }
    // 验证 mock 对象的方法调用
    @Test
    public void testMockVerification() {
        List<String> list = mock(List.class);
        list.add("foo");
        list.add("bar");
        verify(list).add("foo");
        verify(list).add("bar");
        // 验证调用方法的次数
        verify(list, times(2)).add(anyString());
    }
    // 模拟方法抛出异常
    @Test
    public void testMockException() {
        List<String> list = mock(List.class);
        doThrow(new RuntimeException()).when(list).clear();
        assertThrows(RuntimeException.class, () -> list.clear());
    }
}
也可以用注解来声明 Mock 对象,这样更清晰
import static org.mockito.Mockito.*;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
// 启用 Mockito 扩展
@ExtendWith(MockitoExtension.class)
public class MyMockitoTest {
    // 创建一个 List 类型的 Mock 对象
    @Mock
    List<String> mockList;
    // 使用 @InjectMocks,将会自动注入被测试类中所声明的 Mock 对象到这个对象中
    @InjectMocks
    MyService myService;
    @Test
    public void testMock() {
        // 模拟 Mock 对象的行为
        when(mockList.get(0)).thenReturn("foo");
        when(mockList.size()).thenReturn(1);
        String result = myService.doSomething();
        assertEquals("foo", result);
        verify(mockList).get(0);
    }
}
class MyService {
    private List<String> list;
    public MyService(List<String> list) {
        this.list = list;
    }
    public String doSomething() {
        return list.get(0);
    }
}
参考资料
《重构 改善既有代码的设计》
有哪个开源项目,单元测试用例覆盖的比较全的?
业务代码写单元测试的最佳姿势是什么?
单元测试有落地效果好的团队吗?
Modern Best Practices for Testing in Java
Thoughts on efficient enterprise testing (1/6) - Sebastian Daschner
SpringBoot 测试实践 - 1:常用的工具的更多相关文章
- Docker与自动化测试及其测试实践
		
Docker 与自动化测试 对于重复枯燥的手动测试任务,可以考虑将其进行自动化改造.自动化的成本在于自动化程序的编写和维护,而收益在于节省了手动执行用例的时间.简而言之,如果收益大于成本,测试任务就有 ...
 - Golang项目的测试实践
		
Golang项目的测试实践 最近有一个项目,链路涉及了4个服务.最核心的是一个配时服务.要如何对这个项目进行测试,保证输出质量,是最近思考和实践的重点.这篇就说下最近这个实践的过程总结. 测试金字塔 ...
 - 9-MySQL DBA笔记-测试实践
		
第9章 测试实践 在第8章中介绍了测试所需要的理论知识,本章将为读者讲述实际的测试过程.实际测试一般包括硬件测试.MySQL基准测试及应用服务压力测试,下面将分别讲述这三方面的内容.此外,测试工具的选 ...
 - 携程酒店DevOps测试实践
		
作者简介 王幸福,携程酒店研发部高级测试经理,负责无线自动化测试相关工作.在测试框架和平台研发.移动测试.DevOps等领域有着丰富的经验. 如今很多大型互联网公司.创新型企业都在积极地进行DevOp ...
 - SharePoint 2013常用开发工具分享
		
众所周知,一款好的开发工具不仅能提高项目开发效率,而且能够协助开发人员简化开发流程.本文汇总几款SharePoint 2013开发常用开发工具,希望能够对大家有所帮助.如果您有更好的工具,没有包含在本 ...
 - 常用自动化测试工具介绍(支持B/S、C/S)
		
一.功能测试工具1.QTP测试工具 全名HP QuickTest Professional software ,最新的版本为HP QuickTest Professional 11.0 QTP是qui ...
 - Drupal常用开发工具(一)——Devel模块
		
进行 Drupal 开发时有许多模块和工具可供使用,其中最常用的两项便是 Devel 及 Drupal for Firebug.本文和<Drupal常用开发工具(二)——Drupal for F ...
 - SharePoint 2013常用开发工具
		
SharePoint 2013常用开发工具分享 2014-04-01 00:59 by jv9, 589 阅读, 1 评论, 收藏, 编辑 众所周知,一款好的开发工具不仅能提高项目开发效率,而且能够协 ...
 - springboot测试、打包、部署
		
本文使用<springboot集成mybatis(一)>项目,依次介绍springboot测试.打包.部署. 大多数朋友是做后端的,也就是为其他系统或者前端UI提供Rest API服务. ...
 - Linux常用网络工具:路由扫描之mtr
		
除了上一篇<Linux常用网络工具:路由扫描之traceroute>介绍的traceroute之外,一般Linux还内置了另一个常用的路由扫描工具mtr. mtr在某些方面比tracero ...
 
随机推荐
- 2021-08-21:给定一个数组arr,长度为N > 1,从中间切一刀,保证左部分和右部分都有数字,一共有N-1种切法,如此多的切法中,每一种都有:绝对值(左部分最大值 – 右部分最大值)。返回最大
			
2021-08-21:给定一个数组arr,长度为N > 1,从中间切一刀,保证左部分和右部分都有数字,一共有N-1种切法,如此多的切法中,每一种都有:绝对值(左部分最大值 – 右部分最大值).返 ...
 - 都说 C++ 没有 GC,RAII: 那么我算个啥?(赠书福利)
			
*以下内容为本人的学习笔记,如需要转载,请声明原文链接微信公众号「ENG八戒」https://mp.weixin.qq.com/s/7A9-tGZxf4w_7eZl3OUQ4A 学过 Java.C# ...
 - GitHub上SSH keys和Deploy keys的区别
			
平时安装一个git然后去GitHub进行SSH keys 配置最后就开始使用,然后换一台电脑再使用$ ssh-keygen -t rsa -C "your email"生成一个ss ...
 - 【rabbitMQ】-延迟队列-模拟控制智能家居的操作指令
			
这个需求为控制智能家居工作,把控制智能家居的操作指令发到队列中,比如:扫地机.洗衣机到指定时间工作 一.什么是延迟队列? 延迟队列存储的对象是对应的延迟消息,所谓"延迟消息" ...
 - 区间数k大数查询
			
题目 问题描述 给定一个序列,每次询问序列中第 l 个数到第 r 个数中第 K 大的数是哪个. 输入格式 第一行包含一个数 n,表示序列长度. 第二行包含 n 个正整数,表示给定的序列. 第三个包含一 ...
 - 用R来分析洛杉矶犯罪
			
由于微信不允许外部链接,你需要点击文章尾部左下角的 "阅读原文",才能访问文中链接. 洛杉矶市(Los Angeles)或"爵士乐的诞生地(The Birthplace ...
 - 揭秘Spring依赖注入和SpEL表达式
			
摘要:在本文中,我们深入探讨了Spring框架中的属性注入技术,包括setter注入.构造器注入.注解式属性注入,以及使用SpEL表达式进行属性注入. 本文分享自华为云社区<Spring高手之路 ...
 - Educational Codeforces Round 150 (Rated for Div. 2) A-E
			
比赛链接 A 代码 #include <bits/stdc++.h> using namespace std; using ll = long long; bool solve() { i ...
 - Prism导航
			
通常,导航意味着某个Control被添加到UI中,与此同时另一个Control被移除. 简单跳转 新建 UserControl,新建ViewModel,VM需要实现 INavigationAware ...
 - 使用Stable Diffusion生成艺术二维码
			
在数字艺术的世界中,二维码已经从单纯的信息承载工具转变为可以展示艺术表达的媒介.这是通过使用Stable Diffusion的技术实现的,它可以将任何二维码转化为独特的艺术作品.接下来,我们将一步步教 ...