一次单元测试优化的过程总结

简介: 本文将介绍淘宝用户运营平台团队最近在实践单元测试过程中遇到的一个问题。

前言

淘宝原用户增长团队(现用户运营平台团队)是比较早践行单测增量覆盖率的团队,坚持了近两年下来,我们积累了数千个test case,在开发新功能、修改原功能的过程中帮助我们发现了许多问题,显著地提升了代码质量、减少线上故障。在这里郑重地向大家推荐,单测是值得认真做的,开头是痛苦的,但是积累一段时间后,量变就会带来质变。



言归正传,接下来谈一谈最近在实践单测过程中遇到的一个问题。在研发协同平台aone(下文简称aone)的发布流水线中,我们针对单元测试设置了增量代码覆盖率85%和test case 100%通过的流程卡点,在每次发布前,要保证test case完全通过才能提交工单。我们遇到了因并发导致的test case失败,调整并发度导致的单测时间过长,但又影响研发效能的问题。最终在并发度和成功率之间找到了一个平衡点,解决了单测流程降低研发效率的问题。


单侧流水线配置

在单测流程中呢,我们主要用到了JUnit、JaCoCo和Surefire三套工具,通过aone提供的容器自动化运行单元测试,搜集测试报告。下面简单介绍一下这三个工具。
▐  JUnit

java界最大名鼎鼎的单元测试框架,无须多言,会java的应该都知道。
▐  JaCoCo

EclEmma团队开发的开源代码覆盖率统计工具,也是java业内最主流的代码覆盖率统计工具。增量代码覆盖率就是通过该工具进行统计的,全量、增量、按类、包统计都支持,非常灵活。
▐  Maven Surefire Plugin

surefire是maven的一个插件,在maven生命周期的test阶段执行单元测试用例。运行完成后还会生成测试报告,方便用户查看单测情况。



我们利用三种工具,加上aone提供的容器和流水线配置能力,完成了自动化单测的流程和发布卡点校验。



单元实践过程


▐  两个阶段


  • 积累test case时期


在刚刚开始单测时,大家新增的代码都相对比较独立,随着业务的发展、工作职责的调整,单测会不断变复杂,不同的service之间互相交织、单测的维护、运行成本都会增加。我们在这个阶段遇到了一个比较棘手的问题。日常开发过程中,单测都是以类为粒度在本地跑的,都能通过后再去流水线验证,一旦提交到流水线,就会遇到个别case失败的问题,一开始排查起来完全没有思路,test case的失败可以说是随机的,任何一个类的任何一个用例都有可能失败。

image.png

经过分析和排查,得出结论是并发导致的,于是我们限制了并发,做了如下配置,确实解决了这个问题。

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.16</version>
    <configuration>
        <reuseForks>false</reuseForks>
        <forkCount>1</forkCount>
    </configuration>
</plugin>

大家可以留意一下reuseForks和forkCount参数,这时候我们还没有深究两个配置的含义,只是简单的限制了并发,这也为后续的故事埋下了伏笔。


  • test case达到一定规模时期


在完成了test case的初始积累以后,新的问题又随之而来。因为没有并发,test case又很多,所以每次单测运行时长长达50分钟。也严重影响了大家的研发效率。在分秒必争的发布窗口期,经常会出现大家等着单测跑完提交发布单的情况。


image.png

▐  问题


看了上述两个不同阶段反映的问题,本质上就是成功率和实效性的trade off问题,如何能提高并发、提升运行速度的同时保障成功率,这就是我们需要解决的最终命题。


▐  原理和解决方案


上文提到了reuseForks和forkCount参数,这些都是maven-surefire-plugin提供的配置项,把surefire插件研究清楚了,应该就能解决如何兼顾速度和实效性的问题。


  • Surefire配置详解


parallel

jvm内并行执行

过parallel参数开启,可选为methods,classes,both,suites等

其他参数

  1. useUnlimitedThreads,不限制线程数
  2. threadCount,线程数
  3. perCoreThreadCount,每核(默认true,和threadCount组合使用)
  4. parallelTestsTimeoutInSeconds,timeout时间

  1. 设置了parallel后,useUnlimitedThreads或者threadCount必须设置一个,不然会报错
  2. parallel级别还有suitesAndClasses等更复杂的配置项,本文不多探讨


参数示例如下,代表methods级别并发,10条线程执行。

<plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>3.0.0-M7</version>
        <configuration>
          <parallel>methods</parallel>
          <threadCount>10</threadCount>
        </configuration>
      </plugin>
</plugins>


fork

多jvm并行执行

  1. forkCount 最多同时生成的JVM个数,特殊语法是nC,代表n倍的CPU核数,2.5C在4核机器上就是10的意思。
  2. reuseForks 是否重复使用fork出的JVM,true代表一个测试类运行完后,进程继续处理下一个,false代表一个类运行完了JVM销毁,重新生成新的JVM
  3. 默认配置 forkCount=1/reuseFork=true,forkCount设置为0会被自动替换为1


parallel和fork

parallel和fork组合后,就可以有更好的并发效率,也会带来更大的冲突可能。


  • 并发导致case失败原因


surefire的文档原文如下,

image.png

简单说来,就是因为JUnit的实现机制,对于JVM内的线程并发,会出现一些race condition或者其他难以复现的问题;对于forkCount大于1且开启复用的情况,因为测试类是在复用的JVM内,也会因为相同的原因产生并发问题导致测试失败。


  • 结果和建议


在彻底搞清楚surefire的配置原理后,我们回到问题来。经过各种排列组合的尝试,我们得出了比较合适的配置,reuseForks=true/ forkCount=2C,最终效果是每次运行时间在10分钟左右,出错概率较低,通过重跑也能解决。


image.png

小tip

mvn默认是按模块串行的,可开启并行提高整体速度(例:mvn -T 1C clean test),但是在我们的场景下,2000多个test case有1800个都在一个模块里,所以开启并行的效果不大。

其实这个问题没有最优组合,只有最合适的组合。在优化了这个单测耗时最久的应用后,我们又分析了其他几个应用,有的应用test case不多,单测运行时长不长,就没有必要开启并发,优先保证成功率即可;有的应用test case直接相互干扰较小,并发度可以调整得更高……

总的来说,在弄明白了原理之后,还需要具体情况具体分析,“纸上得来终觉浅,绝知此事要躬行”,大家可以分析一下自己应用的情况,结合surefire的并发机制进行实践,相信测过几次以后就能找到最合适的配置组合。



单元实践过程


在整个过程中,笔者还留有两个想法:

  1. 有没有办法通过提高单测代码质量来避免或者降低因为并发引起的失败?一些思路是通过suite分组,将可能冲突的类分开跑,这样的做法可能会极大的提高单测开发成本,投入产出比不高。
  2. test case通过率可以不用严格卡100%,设定到99.5%都能显著的提升效率,因为每次失败的test case是不固定的,所以偶发的个别问题不影响整体的回归。



在实践卓越工程的过程中,笔者深切的感受到纵观整个软件研发的生命周期,有很多值得研究和切入的点,一些微小的改动,都能有效地提升研发效能和交付质量。在当前的环境下,业务竞争日趋激烈,所谓开源节流,“开源”难,重心就会偏向“节流”,降本增效一定会是下一个阶段的重点。而且对于技术人来说,效率一定是永远的追求。其实提升性能、效率往往不是特别高大上的事情,希望大家能在日常繁重的工作之余,有点时间做些有趣的研究,享受技术带来的快乐!



参考资料


  1. https://stackoverflow.com/questions/3365628/junit-tests-pass-in-eclipse-but-fail-in-maven-surefire
  2. https://maven.apache.org/surefire/maven-surefire-plugin/examples/fork-options-and-parallel-execution.html
  3. https://www.baeldung.com/maven-junit-parallel-tests



相关文章
|
3月前
|
人工智能 搜索推荐 数据管理
探索软件测试中的自动化测试框架选择与优化策略
本文深入探讨了在现代软件开发流程中,如何根据项目特性、团队技能和长期维护需求,精准选择合适的自动化测试框架。
165 11
|
3月前
|
定位技术 开发者
游戏开发者如何使用独享静态代理IP进行测试与优化
随着互联网技术的发展,使用代理IP的人数逐渐增加,特别是在业务需求中需要使用静态代理IP的情况越来越多。本文探讨了独享静态代理IP是否适用于游戏行业,分析了其优势如稳定性、不共享同一IP地址及地理位置选择等,同时也指出了需要注意的问题,包括可能的延迟、游戏兼容性和网络速度等。总体而言,选择合适的代理服务并正确配置,可以有效提升游戏体验。
58 2
|
4月前
|
人工智能 前端开发 测试技术
探索软件测试中的自动化框架选择与优化策略####
本文深入剖析了当前主流的自动化测试框架,通过对比分析各自的优势、局限性及适用场景,为读者提供了一套系统性的选择与优化指南。文章首先概述了自动化测试的重要性及其在软件开发生命周期中的位置,接着逐一探讨了Selenium、Appium、Cypress等热门框架的特点,并通过实际案例展示了如何根据项目需求灵活选用与配置框架,以提升测试效率和质量。最后,文章还分享了若干最佳实践和未来趋势预测,旨在帮助测试工程师更好地应对复杂多变的测试环境。 ####
89 4
|
4月前
|
机器学习/深度学习 前端开发 测试技术
探索软件测试中的自动化测试框架选择与优化策略####
本文深入探讨了在当前软件开发生命周期中,自动化测试框架的选择对于提升测试效率、保障产品质量的重要性。通过分析市场上主流的自动化测试工具,如Selenium、Appium、Jest等,结合具体项目需求,提出了一套系统化的选型与优化策略。文章首先概述了自动化测试的基本原理及其在现代软件开发中的角色变迁,随后详细对比了各主流框架的功能特点、适用场景及优缺点,最后基于实际案例,阐述了如何根据项目特性量身定制自动化测试解决方案,并给出了持续集成/持续部署(CI/CD)环境下的最佳实践建议。 --- ####
|
4月前
|
人工智能 监控 测试技术
探索软件测试中的自动化框架选择与优化策略####
【10月更文挑战第21天】 本文深入剖析了软件测试领域面临的挑战,聚焦于自动化测试框架的选择与优化这一核心议题。不同于传统摘要的概述方式,本文将以一个虚拟案例“X项目”为线索,通过该项目从手动测试困境到自动化转型的成功历程,生动展现如何根据项目特性精准匹配自动化工具(如Selenium、Appium等),并结合CI/CD流程进行深度集成与持续优化,最终实现测试效率与质量的双重飞跃。读者将跟随“X项目”团队的视角,直观感受自动化框架选型的策略性思考及实践中的优化技巧,获得可借鉴的实战经验。 ####
55 0
|
5月前
|
Web App开发 前端开发 JavaScript
探索Python科学计算的边界:利用Selenium进行Web应用性能测试与优化
【10月更文挑战第6天】随着互联网技术的发展,Web应用程序已经成为人们日常生活和工作中不可或缺的一部分。这些应用不仅需要提供丰富的功能,还必须具备良好的性能表现以保证用户体验。性能测试是确保Web应用能够快速响应用户请求并处理大量并发访问的关键步骤之一。本文将探讨如何使用Python结合Selenium来进行Web应用的性能测试,并通过实际代码示例展示如何识别瓶颈及优化应用。
254 5
|
5月前
|
缓存 监控 算法
软件测试中的性能瓶颈分析与优化策略
【10月更文挑战第6天】 性能测试是确保软件系统在高负载条件下稳定运行的重要手段。本文将深入探讨性能测试的常见瓶颈,包括硬件资源、网络延迟和代码效率等问题。通过具体案例分析,我们将展示如何识别并解决这些问题,从而提升软件的整体性能。最后,文章还将分享一些实用的性能优化技巧,帮助读者在日常开发和测试中更好地应对性能挑战。
196 3
|
6月前
|
监控 测试技术 持续交付
软件测试中的性能瓶颈分析与优化策略
性能瓶颈,如同潜伏于软件深处的隐形障碍,悄然阻碍着系统的流畅运行。本文旨在揭示这些瓶颈的形成机理,剖析其背后的复杂成因,并汇聚一系列针对性的优化策略,为软件开发者提供一套系统性的解决方案。
98 6
|
5月前
|
运维
【运维基础知识】用dos批处理批量替换文件中的某个字符串(本地单元测试通过,部分功能有待优化,欢迎指正)
该脚本用于将C盘test目录下所有以t开头的txt文件中的字符串“123”批量替换为“abc”。通过创建批处理文件并运行,可实现自动化文本替换,适合初学者学习批处理脚本的基础操作与逻辑控制。
327 56
|
4月前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
102 1

热门文章

最新文章

  • 1
    小鱼深度评测 | 通义灵码2.0,不仅可跨语言编码,自动生成单元测试,更炸裂的是集成DeepSeek模型且免费使用,太炸裂了。
  • 2
    3天功能开发→3小时:通义灵码2.0+DEEPSEEK实测报告,单元测试生成准确率92%的秘密
  • 3
    Potpie.ai:比Copilot更狠!这个AI直接接管项目代码,自动Debug+测试+开发全搞定
  • 4
    【01】噩梦终结flutter配安卓android鸿蒙harmonyOS 以及next调试环境配鸿蒙和ios真机调试环境-flutter项目安卓环境配置-gradle-agp-ndkVersion模拟器运行真机测试环境-本地环境搭建-如何快速搭建android本地运行环境-优雅草卓伊凡-很多人在这步就被难倒了
  • 5
    基于FPGA的图像双线性插值算法verilog实现,包括tb测试文件和MATLAB辅助验证
  • 6
    大前端之前端开发接口测试工具postman的使用方法-简单get接口请求测试的使用方法-简单教学一看就会-以实际例子来说明-优雅草卓伊凡
  • 7
    「ximagine」业余爱好者的非专业显示器测试流程规范,同时也是本账号输出内容的数据来源!如何测试显示器?荒岛整理总结出多种测试方法和注意事项,以及粗浅的原理解析!
  • 8
    用户说 | 通义灵码2.0,跨语言编码+自动生成单元测试+集成DeepSeek模型且免费使用
  • 9
    以项目登录接口为例-大前端之开发postman请求接口带token的请求测试-前端开发必学之一-如果要学会联调接口而不是纯写静态前端页面-这个是必学-本文以优雅草蜻蜓Q系统API为实践来演示我们如何带token请求接口-优雅草卓伊凡
  • 10
    阿里云零门槛、轻松部署您的专属 DeepSeek模型体验测试