【Maven】本地执行一个模块的UT失败,但是线上能执行成功

本文描述了一个项目中遇到的问题,新写的代码UT在本地执行失败但在线上成功,原因是本地的Mockito静态mock未正确关闭,导致线程局部影响。作者通过分析测试步骤和工具版本,强调了使用Mockitostaticmock时必须确保正确的生命周期管理。

背景

在一个工程项目中,往往我们都需要针对新写的代码补充 UT.
基于此,我们每次进一次新的代码,比较好的习惯就是用新的 UT 去覆盖或者旧的UT 去覆盖掉当前新写的 代码。

而就在最近,发现有 UT 不管在本地清理缓存之后,依旧执行失败,但是在线上却都能每次都成功。
并且两边的 UT 执行都是串行的,所以可以很确定的说,这个 UT 肯定在本地有上下文影响的。

原因定位

串行执行,但是顺序不一样?

在确定了串行执行的之后,发现,既然都是串行的,线上和本地的电脑执行的 maven 版本也都对齐了都情况下。

对齐 maven 版本,是为了避免再次遇到奇奇怪怪的问题,在定位问题的时候,如果没有太多的上下文,那么对齐测试步骤和使用工具版本是非常有必要的。

理论上,两边的执行case 的顺序也应该是一样的。
但是很不幸,使用脚本快速确认两边执行的测试用例的顺序是不一样。
预期失败的 case 是在本地最后一个执行的,而在线上 却是在中间就执行了。

万幸的是,确定了顺序是不一样,那么就很快确定,其实影响了case 范围我们也能确定下来。

排除了线上能执行通过的前置的那些case,那么剩下的case 肯定就是能影响到了。

到底是哪个case 影响到了?

确定了影响的范围的 case 到底是哪些呢?是一个还是两个?

一个简单的做法其实就是二分法,将剩下的 影响的case 一半 disable或者说是skip 掉,然后重跑,逐渐缩小其范围,当然我们可以写脚本去触发 ut 执行即可。

最终,确定了影响的case 竟然是这样写代码!

@Test
public void testA() {
	...
	MockedStatic<StaticUtils> utilities = Mockito.mockStatic(StaticUtils.class));
	utilities.when(StaticUtils::name).thenReturn("Eugen");
	assertThat(StaticUtils.name()).isEqualTo("Eugen")
	...
}

有小伙伴看出是什么问题了吗?!

对,乍一看好像什么都没错,但是实际上这个是有大问题的!

因为根据 Maven 官方一些使用文档,着重提醒了如下内容!
参考文档:
maven 使用手册:关于 static mocks
Mocking Static Methods With Mockito

As previously mentioned, since Mockito 3.4.0, we can use the Mockito.mockStatic(Class classToMock) method to mock invocations to static method calls. This method returns a MockedStatic object for our type, which is a scoped mock object.

Therefore, in our unit test above, the utilities variable represents a mock with a thread-local explicit scope. It’s important to note that scoped mocks must be closed by the entity that activates the mock. This is why we define our mock within a try-with-resources construct so that the mock is closed automatically when we finish with our scoped block.

意思简单翻译下就是:如果要用 mock static 那么请注意使用 try-with-resources 的方式,不要让其影响到整个测试流程!

@Test
public void testA() {
	...
	try (MockedStatic<StaticUtils> utilities = Mockito.mockStatic(StaticUtils.class))) {
		utilities.when(StaticUtils::name).thenReturn("Eugen");
		assertThat(StaticUtils.name()).isEqualTo("Eugen")
	}
	...
}

总结

这个 mock static 影响的非常大,但是藏着又特别深,在非常多的 case 当中去找到影响的 case 是非常费力又不讨好的事情。
所以,千万谨记,如果要用相关的 测试方式,请查看人家的官网测试手册给到的建议!!!

番外

为什么线上的 maven 和 本地的maven 的执行 ut case 顺序不一样?
原因在于,线上的执行命令修改了,并且用的排序方式是使用执行测试的方法名字典序排列。
而本地执行顺序是以执行方法类的字典序排列。
这就是一开始的差异点。

我将开发一个大学生实习就业系统,工作任务如下: 系统开发使用Java语言,开发模式采用SpringBoot后台开发和Vue前端框架相结合。系统功能角色包括系统管理员(学校端管理)、学生(学生端模块)和企业(企业招聘管理模块)三个部分。企业招聘管理模块的内容包括系统首页、在线招聘、学生认证、创建简历、简历投递、在线招聘、就业指导、问卷调查等。学校端管理员教师审核企业的平台注册信息,并针对企业上传的招聘信息进行审核,审核通过的信息学生方可看见。负责班级学生实习情况统计,学院领导可查看学院全体学生的实习等。实习系统学生端模块学生通过系统浏览企业发布的招聘岗位,在线制作或上传简历并投递到心仪的企业,参加线上企业招聘会。在企业面试成功、签订协议后,由学校端管理模块对学生协议签订和实际求职情况进行审核,审核通过后,学生可上岗。 我打算使用若依前后端分离版本进行开发,现在我已经搭建好了若依脚手架。在角色管理中创建好了,students和Boss两个角色。 当前我已经建立了学生信息表、学院信息表、班级信息表。 表结构如下: -- ---------------------------- -- Table structure for class_info -- ---------------------------- DROP TABLE IF EXISTS `class_info`; CREATE TABLE `class_info` ( `class_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '班级ID(主键)', `class_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '班级名称(格式如:计算机2021级1班)', `college_id` bigint(20) NOT NULL COMMENT '所属学院ID(外键)', `class_teacher` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '班主任姓名', `student_count` int(11) NULL DEFAULT 0 COMMENT '班级学生人数(统计字段)', `create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` datetime NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`class_id`) USING BTREE, INDEX `idx_college_id`(`college_id`) USING BTREE, CONSTRAINT `fk_class_college` FOREIGN KEY (`college_id`) REFERENCES `college_info` (`college_id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '班级信息表' ROW_FORMAT = Dynamic; SET FOREIGN_KEY_CHECKS = 1; -- ---------------------------- -- Table structure for college_info -- ---------------------------- DROP TABLE IF EXISTS `college_info`; CREATE TABLE `college_info` ( `college_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '学院ID(主键)', `college_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '学院名称(唯一)', `college_leader` varchar(20) CHARACTER SET utf8mb4 COLLATE ut
最新发布
03-12
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值