前言

仔细回想起来,上次认真编写单元测试已经是两年前的事了。那时候觉得写单元测试是种负担。

为了应付代码覆盖率要求,常常依赖工具自动生成测试用例,有时需要启动Spring容器,有时又不需要(当时还分不清单元测试和集成测试的区别)。

直到最近在开发过程中,当需要重构代码或自测功能时,我才真正体会到单元测试的价值。

概念

单元测试的本质是通过独立的测试用例来验证代码单元(方法/函数)的逻辑正确性。

在日常开发中,我们经常会在类里随手写一个 main 方法来快速验证逻辑,比如:

public static void main(String[] args) {
int expectResult = 9;
int result = 3 * 3;
if (result == expectResult) {
System.out.println("测试成功");
} else {
System.out.println("测试失败");
}
}

测试分类

首先介绍一下,单元测试与集成测试的区别,刚开始学习的时候,没有分清它俩的区别,导致一直感觉单元测试是一个很复杂的东西。

集成测试

需要启动完整的 Spring 容器,容器内所有的 Bean 可以正常注入使用。通俗一点,相当于写了一个 TestController。

@SpringBootTest  // 启动完整Spring容器
class UserControllerIntegrationTest {
@Autowired // 真实注入Bean
private UserController userController; @Test
void testGetUser() {
User user = userController.getUser(1L);
assertNotNull(user);
}
}

单元测试

不启动 Spring 容器,所有依赖需要手动模拟

@ExtendWith(MockitoExtension.class)  // 启用Mockito注解支持
class UserServiceUnitTest { @Mock
private UserRepository userRepository; @InjectMocks
private UserService userService; @Test
void testGetUser() {
// 1. 定义Mock行为
when(userRepository.findById(1L))
.thenReturn(new User(1L, "Mock用户")); // 2. 执行测试
User user = userService.getUser(1L); // 3. 验证结果
assertEquals("Mock用户", user.getName()); // 4. 可选:验证Mock交互
verify(userRepository).findById(1L); // 确保findById被调用一次
}
}
维度 单元测试 集成测试
目标 验证单个代码单元(如方法、类)的功能正确性 验证多个模块、组件或外部系统协同工作的正确性
范围 隔离测试,不依赖外部系统(如数据库、网络、其他服务) 依赖外部系统或跨组件的交互(如数据库连接、API 调用)
覆盖范围 聚焦分支,验证代码片段逻辑正确性 聚焦模块间交互,验证系统整体行为

核心注解介绍

@Resource (Spring标准注解)

作用:用于依赖注入,会按照名称或类型从Spring容器中获取真实的bean

测试场景:集成测试中需要完全使用真实逻辑时

@MockBean (Spring Boot测试注解)

作用:向Spring应用上下文注入一个mock对象,替代原有的bean

测试场景:Spring Boot集成测试中需要mock某些bean时

@Spy (Mockito注解)

作用:创建部分mock对象,未mock的方法使用真实逻辑,mock的方法使用自定义逻辑

测试场景:需要保留对象大部分真实行为,仅修改少数方法逻辑的测试

@Mock (Mockito注解)

作用:创建完整mock对象,所有方法都需要mock,未mock的方法会返回默认值或空集合

测试场景:需要完全模拟依赖行为的单元测试

@InjectMocks (Mockito注解)

作用:自动将@Mock或@Spy创建的mock对象注入到被测试对象中

测试场景:需要自动装配依赖的单元测试

使用

依赖文件

添加依赖,具体版本需要根据项目版本自己替换

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>

单元测试

@Spy 与 @Mock 的区别:

@Mock 所有方法都需要 mock,未 mock 的方法会返回默认值或空集合 ; @Spy 未 mock 的方法使用真实逻辑,mock 的方法使用自定义逻辑

注意:没有无参构造会初始化为空指针,需要手动实例化进行解决

@Spy
private UserService userService = new UserService(); // 显式提供实例

集成测试

@MockBean:向Spring上下文注入 Mock对象

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*; @SpringBootTest
public class UserServiceTest {
@Autowired
private UserService userService; // 注入真实 UserService @MockBean
private UserRepository userRepository; // 替换 Spring 容器中的 UserRepository @Test
public void testGetUser() {
// 配置 Mock 行为
User mockUser = new User(1, "Alice");
when(userRepository.findById(1)).thenReturn(Optional.of(mockUser)); // 调用被测方法
User result = userService.getUser(1); // 验证结果和交互
assertEquals("Alice", result.getName());
verify(userRepository).findById(1);
}
}

总结

更多的内容还需要在探索后补充

Java 单元测试简单扫盲的更多相关文章

  1. Java单元测试技术1

    另外两篇关于介绍easemock的文章:EasyMock 使用方法与原理剖析,使用 EasyMock 更轻松地进行测试 摘要:本文针对当前业软开发现状,先分析了WEB开发的技术特点和单元测试要解决的问 ...

  2. 转载-使用 Feed4JUnit 进行数据与代码分离的 Java 单元测试

    JUnit 是被广泛应用的 Java 单元测试框架,但是它没有很好的提供参数化测试的支持,很多测试人员不得不把测试数据写在程序里或者通过其它方法实现数据与代码的分离,在后续的修改和维护上有诸多限制和不 ...

  3. Java单元测试工具:JUnit4(一)(二)(三)(四)

    Java单元测试工具:JUnit4(一)--概述及简单例子 Java单元测试工具:JUnit4(二)--JUnit使用详解 Java单元测试工具:JUnit4(三)--JUnit详解之运行流程及常用注 ...

  4. Java单元测试(Junit+Mock+代码覆盖率)

    微信公众号[程序员江湖] 作者黄小斜,斜杠青年,某985硕士,阿里 Java 研发工程师,于 2018 年秋招拿到 BAT 头条.网易.滴滴等 8 个大厂 offer,目前致力于分享这几年的学习经验. ...

  5. Java单元测试框架 JUnit

    Java单元测试框架 JUnit JUnit是一个Java语言的单元测试框架.它由Kent Beck和Erich Gamma建立,逐渐成为源于KentBeck的sUnit的xUnit家族中为最成功的一 ...

  6. 原!!关于java 单元测试Junit4和Mock的一些总结

    最近项目有在写java代码的单元测试,然后在思考一个问题,为什么要写单元测试??单元测试写了有什么用??百度了一圈,如下: 软件质量最简单.最有效的保证: 是目标代码最清晰.最有效的文档: 可以优化目 ...

  7. 有效使用Mock编写java单元测试

    Java单元测试对于开发人员质量保证至关重要,尤其当面对一团乱码的遗留代码时,没有高覆盖率的单元测试做保障,没人敢轻易对代码进行重构.然而单元测试的编写也不是一件容易的事情,除非使用TDD方式,否则编 ...

  8. Java单元测试(Junit+Mock+代码覆盖率)---------转

    Java单元测试(Junit+Mock+代码覆盖率) 原文见此处 单元测试是编写测试代码,用来检测特定的.明确的.细颗粒的功能.单元测试并不一定保证程序功能是正确的,更不保证整体业务是准备的. 单元测 ...

  9. Java实现简单版SVM

    Java实现简单版SVM 近期的图像分类工作要用到latent svm,为了更加深入了解svm,自己动手实现一个简单版的.         之所以说是简单版,由于没实用到拉格朗日,对偶,核函数等等.而 ...

  10. java实现简单的单点登录

    java实现简单的单点登录 摘要:单点登录(SSO)的技术被越来越广泛地运用到各个领域的软件系统当中.本文从业务的角度分析了单点登录的需求和应用领域:从技术本身的角度分析了单点登录技术的内部机制和实现 ...

随机推荐

  1. linux命令操作android手机

    目的 通过一台linux机器操作android手机做一些常用的操作 复杂的操作都是由简单操作开始的, 可以自行发掘 环境 笔记本: thinkpad t480 操作系统: archlinux adb版 ...

  2. 安全可信 | 首批!天翼云边缘安全加速平台AccessOne通过信通院“软件自研创新能力”专项评估

    近日,中国信息通信研究院(以下简称"中国信通院")公布"软件自研创新能力"专项评估(简称"可信研创")结果,天翼云边缘安全加速平台Acces ...

  3. Prometheus修改数据存储位置

    Prometheus修改数据存储位置 Prometheus的数据存储位置可以通过配置文件中的 --storage.tsdb.path 参数来指定.默认情况下,数据存储在Prometheus安装目录下的 ...

  4. FANUC发那科工业机器人减速器维修小细节

    在现代工业生产中,FANUC发那科机器人已成为不可或缺的一部分.然而,随着时间的推移,发那科机械手减速器可能会出现故障,影响机器人的正常工作. 一.了解减速器的结构与工作原理 在开始FANUC发那科机 ...

  5. 泰山派(Ubuntu 20.0)更换软件源

    泰山派更换软件源 1.编辑apt软件包源获取文件 vim /etc/apt/sources.list 2.更换为下面的源 deb http://mirrors.ustc.edu.cn/ubuntu-p ...

  6. Flink学习(十三) Flink 常见核心概念分析

    分布式缓存熟悉 Hadoop 的你应该知道,分布式缓存最初的思想诞生于 Hadoop 框架,Hadoop 会将一些数据或者文件缓存在 HDFS 上,在分布式环境中让所有的计算节点调用同一个配置文件.在 ...

  7. 什么是git,什么是github,git和github的使用

    Git实战 注意:本项目是学习笔记,来自于哔哩哔哩武沛齐老师的Git实战视频, 网址:[武沛齐老师讲git,看完绝对上瘾!!!] https://www.bilibili.com/video/BV1n ...

  8. Linux系列:如何用 C#调用 C方法造成内存泄露

    一:背景 1. 讲故事 好久没写文章了,还是来写一点吧,今年准备多写一点 Linux平台上的东西,这篇从 C# 调用 C 这个例子开始.在 windows 平台上,我们常常在 C++ 代码中用 ext ...

  9. IOS 内付 asp.net mvc 服务器端验证

    上代码: public class AppStorePayApp { public int VerifyReceipt(string receipt, out string product_id, o ...

  10. Qt QCheckBox设置复选框的大小

    文章目录 Qt设计QCheckBox样式表 QCheckBox的各部分代表的样式表 Qt QCheckBox设置复选框的大小 Qt设计QCheckBox样式表 QCheckBox的各部分代表的样式表 ...