单测结果不稳定的终极解决方案(Maven单测参数调优)

一、前言

近期,在公司平台执行单测任务时,我发现到一个显著的问题:我们的一个应用,在公司平台上执行单测时,即使是相同的代码,每次的执行结果(包括行覆盖率以及单测通过率)都存在差异。更具体地说,许多在本地环境中能够成功执行的测试用例,在公司平台上却遭遇了失败。

为了解决这个问题,我进行了广泛的信息搜索,咨询了 ChatGPT,并尝试了多种可能的解决方案。经过不懈的努力,我终于找到了问题的根源并成功解决了它。在接下来的内容中,我将与大家分享这个问题的解决过程以及导致问题的根本原因。

二、问题解决过程

1、问题概述

在公司平台执行单元测试过程中,一个应用表现出了显著的测试结果波动性。不仅测试通过率波动较大,而且每次执行的测试用例数量也呈现不一致性。

2、问题解决过程

A、方案1:去掉并行执行

遇到问题时,我们首先考虑的是寻求平台团队的支持。平台团队给出的反馈是,我们的单元测试可能不适合并发执行(这是一个很好的思路)。由于平台默认启用了单元测试的并发执行功能(参数-T 2C)。如果我们希望关闭并发执行,只需删除改参数配置即可(有关该参数的详细说明将在下一节中提供)。

在移除并行调用参数后,我对单元测试进行了多次重新执行,但遗憾的是,结果并未出现预期的改善。

B、方案二:正确使用静态类mock

在上述方案未能取得预期效果后,我继续深入分析单元测试结果。通过对比两次执行结果生成的单元测试报告,我发现执行成功率较低的报告中记录了大量的静态mock错误,这些错误在执行成功率较高的报告中并未出现:static mocking is already registered in the current thread To create a new mock, the existing static mock registration must be deregistered。

鉴于我们当前使用的是Mockito 4.X版本,我们采用mockStatic方法来模拟静态类。由于许多团队成员对这种mockStatic的使用方式尚不熟悉(之前习惯使用PowerMock),因此在使用过程中可能会出现不规范的情况。熟悉Mockito.mockStatic方法的同事应该都知道,一旦启用了mockStatic,确保在测试结束后正确关闭它是非常重要的。因此,我们通常建议使用try和finally块来确保mockStatic的开启和关闭,具体操作如下所示。如果没有按照这种方式使用,就可能会遇到前述的静态mock错误。

随后,我着手修正所有的错误用法。在完成这些修正后,我再次在平台上多次执行单元测试用例。最终发现,虽然静态mock的错误得到了解决,但单元测试的结果仍然不够稳定,尽管整体表现有所改善。

C、方案三:禁用fork进程重用

在上面方案未能取得成功后,我继续探索其他可能的解决方案。最终,我转向了Maven的官方文档,寻找关于单元测试并行执行的参数调优信息。在阅读了Maven官方文档中关于并行执行的参数调整部分(https://maven.apache.org/surefire/maven-surefire-plugin/examples/fork-options-and-parallel-execution.html)后,我了解到,即使去除了最初方案中的并行参数,Maven默认仍然提供了一种提高单元测试用例执行速度的方式,即通过设置`reuseForks`参数。

默认情况下,Maven会设置reuseForks=true/forkCount=1,这意味着Maven将创建一个进程来执行单元测试,并且所有的单元测试都会通过这个进程运行。这种方式通常没有问题,但是如果单元测试之间使用了公共资源(如上下文、静态变量和静态配置等),则可能会导致进程间相互影响。因此,我将reuseForks设置为false,让Maven为每个单元测试类创建一个新的进程来执行。这样修改后,我连续执行了几次单元测试,发现结果非常稳定,而且整体的单元测试结果也有所提升。

3、根因总结

禁用进程复用功能后,单元测试结果的不稳定问题得到解决,这表明确实存在单元测试用例之间使用公共资源的问题。由于单元测试用例数量庞大,没有足够的时间去逐一分析每个用例中具体存在哪些公共资源的使用问题。

三、Maven单测参数调优

1、默认参数(单进程复用)

设置:默认情况下,`reuseFork`设置为`true`,这意味着Maven会尝试复用已经创建的进程来执行后续的测试用例。

执行方式:所有测试用例会依次在一个进程中串行执行。

可能的问题:如果测试用例之间有依赖关系,可能会导致测试结果不稳定。

2、Module维度并发执行(线程维度)

设置:通过命令行参数`-T 2`或`-T 2C`来设置线程数。`-T 2C`表示线程数等于CPU核心数的两倍。

执行方式:如果有多个模块,每个模块的测试会用一个线程并发执行。

可能的问题:如果模块间的测试用例有资源竞争,可能会导致测试结果不稳定。

3、单测维度并发执行(线程维度)

设置:使用`parallel`参数,可以设置在类维度(`classes`)或方法维度(`methods`)并发执行。还可以设置线程池大小等参数。

执行方式:测试用例会在类或方法维度上并发执行。

可能的问题:如果测试用例之间存在资源竞争,可能会导致测试结果不稳定。

4、Fork进程方式并发执行(进程维度)

设置:通过设置`forkCount=N`和`reuseFork=true`(默认为`true`)来开启多进程并发执行。

执行方式:每个测试用例类会在一个独立的进程中执行。

可能的问题:如果测试用例类之间有资源竞争,可能会导致测试结果不稳定。

在使用这些并发执行方式时,需要注意的是,虽然它们可以显著提高测试的执行效率,但同时也会增加测试用例之间相互干扰的风险。因此,在采用这些并发执行方式之前,应该确保测试用例之间是相互独立的,没有共享资源或状态。

在实际应用中,可能需要结合项目的具体情况和测试用例的特点,选择最合适的并发执行策略。如果测试用例之间存在共享资源的问题,可能需要采取额外的措施,如使用测试数据库、配置文件副本、环境隔离等,以确保测试的稳定性和可靠性。

5、如何选择

根据不同的需求和场景,我们需要在单元测试的并发执行方面做出合理的选择。

A、单元测试支持并发执行

可以同时开启Module维度并发和单测维度并发。这样可以最大化地利用多核CPU的能力,提高测试的执行效率。但是,需要确保测试用例之间没有共享资源,以避免并发执行带来的潜在问题。

B、单元测试不支持并发,但是可以接受单测结果的波动

可以合理调整并发线程数(调小一点),并开启并发。这样做可以平衡执行速度和结果稳定性。线程数越少,潜在的并发冲突和数据竞争就越小,从而减少测试结果的波动。

C、单元测试不支持并发,但是需要单测结果稳定

应该关闭并发执行。在这种情况下,可以只使用默认的单进程复用设置,确保测试用例按顺序串行执行,从而保证测试结果的稳定性。

D、在默认值的基础上,仍然存在不稳定的情况

考虑关闭`reuseFork`。如果测试用例之间存在共享资源,可能会导致测试结果不稳定。关闭`reuseFork`可以确保每个测试用例都在新的进程中执行,从而减少进程间的相互影响。

总的来说,选择哪种并发执行策略取决于项目需求、测试用例的设计以及可接受的测试结果稳定性。从执行速度来看,A选项通常最快,因为它是最大程度地利用了并发执行的优势,而D选项通常最慢,因为它完全避免了进程复用,每个测试用例都在新的进程中执行。在实际应用中,可能需要根据测试结果和项目进度进行多次调整,以达到最佳的测试效果。

四、附录

单测并发执行(Maven官方)

Maven Surefire Plugin – Fork Options and Parallel Test Execution

五、惯例

如果你喜欢本文或觉得本文对你有所帮助,欢迎一键三连支持,非常感谢。
如果你对本文有任何疑问或者高见,欢迎添加公众号lifeofcoder共同交流探讨(添加公众号可以获得楼主最新博文推送以及”Java高级架构“上10G视频和图文资料哦)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/596463.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android kotlin build.gradle.kts配置

1. 添加 maven 仓库 1. 1. settings配置 1. 1.1. settings.gradle repositories {maven {url https://maven.aliyun.com/repository/public/}mavenCentral() }1. 1.2. settings.gradle.kts repositories {maven {setUrl("https://maven.aliyun.com/repository/public/…

《Redis实战》学习笔记

特点 :1、是一个高性能的key/value内存型数据库 2、支持丰富的数据类型(string,List,Set,ZSet,Hash) 3、支持持久化 内存数据, 可以持久化到硬盘中 4、单进程,单线程 效率高 redis实现分布式锁 一、redis的相关指令 1、flushDB 清空当前…

LDD学习笔记 -- Linux设备驱动概述

LDD学习笔记 -- Linux设备驱动概述 概述分类字符设备驱动块设备驱动 设备文件 概述 设备驱动:配置和管理设备的一段代码。 负责与硬件设备进行交互,并导出应用程序和其他内核模块可以用来访问设备的接口。 该代码能够通过向设备发送数据来配置设备&am…

Flink实时电商数仓之旁路缓存

撤回流的处理 撤回流是指流式处理过程中,两表join过程中的数据是一条一条跑过来的,即原本可以join到一起的数据在刚开始可能并没有join上。 撤回流的格式: 解决方案 定时器:使用定时器定时10s(数据最大的时间差值&am…

7.14解数独(LC37-H)

算法: 二维递归(递归时需要两层for循环) 一个for循环放行 另一个for循环放列 画树: 因为这个树形结构太大了,我抽取一部分,如图所示: 回溯三部曲: 1.确定函数参数和返回值 返…

在Gradle工程中使用checkstyle来规范你的项目

🌹作者主页:青花锁 🌹简介:Java领域优质创作者🏆、Java微服务架构公号作者😄 🌹简历模板、学习资料、面试题库、技术互助 🌹文末获取联系方式 📝 系列专栏目录 [Java项…

CSS transition详解

文章目录 属性transition-propertytransition-durationtransition-timing-functiontransition-delaytransition 简写属性 方法Element:transitionrun 事件Element:transitionstart 事件Element:transitionend 事件Element:transit…

音频DAC,ADC,CODEC高性能立体声

想要让模拟信号和数字信号顺利“交往”,就需要一座像“鹊桥”一样的中介,将两种不同的语言转变成统一的语言,消除无语言障碍。这座鹊桥就是转换器芯片,也就是ADC芯片。ADC芯片的全称是Analog-to-Digital Converter, 即模拟数字转换…

【白盒测试】逻辑覆盖和路径测试的设计方法

📢专注于分享软件测试干货内容,欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!📢交流讨论:欢迎加入我们一起学习!📢资源分享:耗时200小时精选的「软件测试」资…

互联网演进历程:从“全球等待”到“全球智慧”的技术革新与商业变革

文章目录 一、导言二、World Wide Wait (全球等待)阶段1. 技术角度2. 用户体验3. 企业收益4. 教育影响 三、World Wide Web (万维网)阶段1. 技术角度2. 用户体验3. 企业收益4. 教育影响 四、World Wide Wisdom (全球智慧)阶段1. 技术角度2. 用户体验3. 企业收益4. 教育影响 五、…

Vue 3.4 正式版发布,带来多项更新

12 月 28 日,Vue 3.4 正式版发布,代号为“🏀 Slam Dunk”,即灌篮高手。据尤大接收,这个版本进行了许多重要的内部改进,其中最引人瞩目的是重写的模板解析器。新的解析器将速度提高了 2 倍,显著提升了整体性能。 此外,响应性系统也经过了重构,使得 effect 触发更为精…

Java编程中的IO模型详解:BIO,NIO,AIO的区别与实际应用场景分析

IO模型 IO模型就是说用什么样的通道进行数据的发送和接收,Java 共支持3种网络编程IO 模式:BIO,NIO,AIO BIO(Blocking lO) 同步阻塞模型, 一个客户端连接对应一个处理线程 代码示例: package com.tuling.bio; import java.io.…

SQL BETWEEN 操作符

BETWEEN 操作符选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。 SQL BETWEEN 语法 SELECT column1, column2, ... FROM table_name WHERE column BETWEEN value1 AND value2; 参数说明: column1, column2, ...:要选择的字段名…

DeepL翻译器,一直想使用怎么办?

作为一个独立开发者,将应用程序翻译到不同语言是个让我很头大的事情。请专业人员翻译太贵无法承受,谷歌翻译质量太差时常词不达意。 如何使用 DeepL 使用起来很直观,打开此网页粘贴要翻译的内容即可。它也支持 macOS 和 PC 端。 这里开我们开…

LinuxShell

一、 新建用户 在Linux上新建一个用户并赋予超级用户权限,建立家目录并设置默认shell为bash,并设置Linux在输入sudo密码时显示星号。请提交全部命令及输出截图(表明完成需求即可)。 1.sudo useradd -m ymhs(用户名) 增加用户 2.su…

Transformer:Attention机制、前馈神经网络、编码器与解码器

主要介绍Transformer的一些工作原理与优势。 文章目录 Transformer中的Attention机制 一、引言 二、Transformer中的Attention机制 1. 背景介绍 2. 工作原理 3. 优势分析 Transformer中的前馈神经网络 一、引言 二、神经网络的基本概念 三、前馈神经网络 四、Transformer中的前…

SpingBoot的项目实战--模拟电商【5.沙箱支付】

🥳🥳Welcome Huihuis Code World ! !🥳🥳 接下来看看由辉辉所写的关于SpringBoot电商项目的相关操作吧 目录 🥳🥳Welcome Huihuis Code World ! !🥳🥳 一. 沙箱支付是什么 二.Sp…

2分钟了解什么是socket?

文章目录 概念比喻类型Socket 与 TCP、UDP的关系 概念 Socket 是提供网络通信功能的编程接口(API),提供了网络通信的基本操作,允许程序或进程之间进行数据交换。是传输层协议的具体软件实现,它封装了协议底层的复杂实…

【干货】Windows中定时删除system32目录下的.dmp文件教程

旭帆科技的技术人员除了给用户答疑解惑以外,还会主动测试软件性能,进行平台优化,除此之外,技术人员还会总结一些技术干货,这不,近期又提供了一份如何在Windows中定时删除system32目录下的.dmp文件的教程。感…

CRM软件对企业发展起着哪些作用?CRM的功能解析

虽然不少科技成果昙花一现,但CRM管理系统作为销售和营销领域的核心技术,已经牢牢占据了不可撼动的地位。拥有一个部署得当的CRM系统能为企业带来诸多好处。它可以跟踪和管理销售人员与潜在/现有客户的所有互动和沟通,并帮助他们识别出需要重点…