前言

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

为了应付代码覆盖率要求,常常依赖工具自动生成测试用例,有时需要启动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. C#中的内部函数(子函数)

    private bool GetMedicalAdvice(string zyID, out DataTable dtM, out DataTable dtD) { bool result = fal ...

  2. Dummynet简单部署

    本文分享自天翼云开发者社区<Dummynet简单部署>,作者:凸凹 部署流程 ^准备内核版本 ^参看系统内核版本 uname -r 我们需要将ipfw编译成内核模块,请确保ipfw用到的内 ...

  3. 对外提供API,通过appId、appSecret、sign秘钥对接口做鉴权

    一.背景 在接口开发过程中,我们通常不能暴露一个接口给第三方随便调用,要对第三方发来参数进行校验,看是不是具有访问权限. 名词介绍: 1.appId: 应用id,用户自定义命名,如:*-access- ...

  4. 基于iscsi存储池

    命令行 [root@kvm1 ~]# virsh pool-define-as --name stor2 --type iscsi \ > --source-host 192.168.114.1 ...

  5. Java中int、Integer、long、Long、double、Double和BigInteger的关系

    Java中int.Integer.long.Long.double.Double和BigInteger的关系 在Java中,int.Integer.long.Long.double.Double和Bi ...

  6. 每次下载idea都必装的十个插件!

    IDEA必备插件 Alibaba Java Coding Guidelines 功能: 阿里巴巴Java开发规范插件,用于代码规范检查. 特点: 基于阿里巴巴Java开发手册,提供实时代码规范检查,帮 ...

  7. FANUC机器人M-16iB伺服马达维修参考措施

    随着工业自动化技术的不断发展,机器人已经广泛应用于各个领域.其中,发那科机器人以其卓越的性能和稳定性,成为了许多企业的首选.然而,伺服电机作为机器人核心部件之一,FANUC机械手维修保养至关重要. 一 ...

  8. Linux挂载U盘,SD卡

    Linux挂载U盘,SD(TF)卡 1.插入U盘,执行如下指令后能看到设备则说明连接成功 sudo fdisk -l #查看外接设备名称,一般为/dev/sd...,这里假设为/dev/sdc1 2. ...

  9. Typecho的Joe主题开启文章导航目录树

    引言 发现从typora复制过来的markdown代码中的目录导航[toc]语句没生效, 没有像typora或其他markdown编辑器生成导航目录树, 网上搜了下, 发现个解决方法, 在主题设置里插 ...

  10. docker - [05] 部署Nginx

    题记部分 一.查找镜像 docker search nginx 二.拉取镜像 docker pull nginx 三.启动镜像 Nginx默认端口号为80,可以在启动时指定Nginx使用的端口号(例如 ...