研究了50万次代码提交的自动化测试数据后,他们有了这些发现

·  阅读 723
[更多精彩文章,欢迎关注公众号“测试不将就”——插上自动化/AI的翅膀,软件测试也能高大上]


PART 1

最近阅读了一篇关于自动化测试的前沿论文,颇受启发。这里做一下“搬运工”,把论文内容粗略分享给大家。这是一篇发表在2017 ICSE(IEEE国际软件工程大会)上,题目为《Taming Google-Scale Continuous Testing》,由马里兰大学计算机系学者和Google研究员、工程师共同完成。马里兰大学是全美著名的高校,“公立常春藤”之一,也是Google创始人谢尔盖·布林的母校。Google是一家能将研究(Research)和工程(Engineering)完美结合的伟大公司,在测试自动化领域更是执牛耳者(一年一度的GTAC谷歌测试自动化大会是领域内的旗舰会议之一)。深厚的背景,决定了论文必然出手不凡,单单标题就可谓霸气十足:《驯服谷歌规模的持续测试》。那么,这篇论文(关注公众号,回复数字“1”获取PDF全文)到底研究了什么问题?取得了什么研究成果呢?


PART 2

持续测试是测试自动化的一个重要应用。什么是持续测试?一般认为,持续测试是一种运行在持续集成(Continuous Integration, CI)系统中的,由开发人员的代码提交所触发的,目的是验证代码改动是否满足质量(Quality)要求的自动化测试。在本公众号之前的文章《Hard Problems in Test Automation》中,我们总结了持续测试的“三高”特性,意图论证:持续测试是一件非常有挑战性的工作


这种挑战性相当程度上来自于客观上存在的测试资源和测试需求之间的矛盾。测试资源(例如测试工具、测试环境等)是有限的。而在持续测试的情况下,测试需求随着项目规模的扩大而成几何级数增加:大量的开发人员高频率地提交代码,并且希望每次代码提交均得到充分的、不留边角的测试;另外,开发人员对测试时间的需求也很强烈,他们希望快速地得到测试反馈结果,以决定下一步行动。巨大、苛刻的测试需求与有限的测试资源之间必然有着严重的矛盾。确实,在大规模软件项目开发中自动化持续测试往往是一个巨大的瓶颈


在谷歌,这种矛盾的程度更是史无前例。这是由于谷歌采用“One Single Repository”战略:全公司几乎所有产品线的代码均集中在一个代码库(极少数敏感项目除外)。据统计(数据来自论文《Why Google Stores Bilions of Lines of Code in a Single Repository》,发表在著名期刊Communications of the ACM上),在2015年1月份,关于这个代码库的数据是:

  • 总文件大小86TB(不包括release分支),

  • 总共有900万个源文件,

  • 总共有20亿行源代码,

  • 历史以来一共产生3500万次提交,

  • 平均每个工作日产生40000次有效提交,

  • 总共25000名开发人员工作在这个代码库上。

这真是一个“航空母舰”级的软件项目,是否前无古人,后无来者?它对持续测试的需求之高简直不可想象。对此,杰出的谷歌工程师们广泛使用了各种自动化测试先进技术。例如,使用回归测试集选择技术,在每次代码提交时,并不去执行全部测试集,而是根据测试用例和被修改文件的依赖性关系,只从回归测试集中选择一部分可能受到影响的用例执行。即使这样,由于代码库极端庞大,最坏情况下修改某一个文件需要执行420万个测试用例。即使在顶级的硬件环境下使用分布式并发执行技术,仍然需要9个小时才能获得测试结果。


即使“最厉害的人” + “最好的资源” + “最先进的技术”,持续测试依然是开发流程中的瓶颈。怎么办?还有哪里可以改进?



PART 3

要想找到哪里可以改进,不能靠蒙不能靠猜(十猜九错),只能基于数据。

幸运的是,论文作者们拥有宝贵的数据。他们拥有一个包含上述代码库在一段时间内的连续50万次代码提交记录及其持续测试结果。数据集中的样本包含代码提交信息(作者信息,改动文件列表,修改时间等)和测试结果信息(影响了哪些测试用例,哪些测试用例执行成功,哪些测试用例执行失败等)。通过对数据集进行统计和分析,作者发现了一些有趣的现象。在介绍这些现象之前,为了简化表述,我们将代码提交(改动)称为Change List, CL,将能够发现CL产生了Bug或者证CL修复了Bug的测试用例称为有效(测试)用例(这种用例起到了测试的作用),将被测试发现产生了Bug或者被测验证修复了Bug的CL称为目标CL(这种CL是测试感兴趣的目标)。

  1. 只有1.23%的用例属于有效测试用例。具体来说,在556万个测试用例中,从不失败,一直成功的测试用例有508万个。至少失败一次并且至少成功一次的用例是我们非常关心的,一共有11.5万个。而其中又包括4.7万个不稳定的测试用例(Flaky Tests)。所谓不稳定的用例,就是相同情况下一会儿成功一会儿失败的用例。这种用例无法用来判断某个CL是否产生了Bug或者修复了Bug。除去Flaky Tests,作者认为剩余的6.8万用例真正起到了作用,占总用例的比例是1.23%。这是一个有趣的数字,它说明自动化持续测试的负载有显著降低的空间。比如,如果降低另外98.77%测试用例的执行频率,我们有望在不影响测试效果的前提下,大大节省测试资源,缩短测试反馈时间。

  2. 当一个CL包含了频繁修改的源文件时,它更有可能是目标CL。论文研究了源文件的修改次数,与源文件出现在目标CL中的概率的关系。研究发现,目标CL中修改次数为1次的源文件,占总的修改次数为1次的源文件的比例是0.07%;而目标CL中修改次数为41次的源文件,占总的修改次数为41次的源文件的比例是75%。一般来说,修改次数越多,这个比例就越大。这意味着,如果一个CL中修改了一个或多个在过去被修改过很多次的文件,那么这个CL有更大可能性会产生Bug。这个发现的意义是两方面的。一方面,持续集成系统在识别到这种CL之后,可以优先调度测试执行,更快地给出测试反馈结果;另一方面,可以在开发环境(IDE)中部署一个插件,当开发人员修改代码时,就给出警告:“您正在修改一个过去被修改了41次的文件,这样有75%的概率会产生Bug”。

  3. 当一个CL包含了某些后缀的源文件时,它更有可能是目标CL。论文研究了不同类型的源文件,目标CL集合中的这种源文件的数量,和数据集中这种源文件的数量的比值。研究发现,不同的源文件类型,其在目标CL中出现的概率是有差异的。例如,后缀为.hpp的文件类型,这个比值超过80%;后缀为.json, .word等13种文件类型,这个比值是0%。两种最广泛使用的编程语言C++和Java,其概率分别是40%和60%。这意味着,C++程序员需要意识到自己相比Java程序员可能有更大概率产生Bug。另外,当开发人员修改代码时,IDE也可以给出警告:“您正在修改一个文件类型X,历史数据表明这个文件类型导致了Google 60%的Bug,建议您使用静态检查工具发现潜在的问题,并更加充分地做代码评审”。

  4. 当一个CL是由某些作者提交的时,它更有可能是目标CL。论文研究了不同开发人员,其提交的目标CL数量与其总共提交的CL数量的比值。研究发现,不同程序员,这个比值也有差异。例如,开发人员A,一共提交了182个CL,其中59个被发现有Bug,比率是31.4%;开发人员B,一共提交了1564个CL,其中214个被发现有Bug,比率是13.7%。这种差异,可能是由于开发人员的编程专业水平有高低,也可能是由于开发人员所从事项目的复杂性或难度有不同。如果将这个发现与IDE集成在一起,那么可以在某个开发人员的IDE上,产生一条提醒:“最近的开发历史显示您有43%的CL产生了Bug。我们建议您在正式提交代码之前,先在本地进行充分的测试”。

  5. 当一个CL包含被多个开发人员修改的源文件时,它更有可能是目标CL。论文研究了被多个开发人员修改了的源文件,其在目标CL中出现的概率。研究发现,当文件被两个人修改时,这个概率比文件被一个人修改时低。作者认为这可能是由于两个人可以很好地相互评审代码;而当文件被三个或者更多人修改时,这个概率显著增加。作者猜测,这可能是由于这种情况下开发人员之间的沟通变得困难,从而难以很好地评审代码。当一个文件被15个不同的程序员修改过时,包含这个文件改动的CL几乎100%被发现有Bug。利用这个发现,我们可以从两方面做改进。一是让持续集成系统优先调度针对这种CL的测试,从而更快地给开发人员反馈;二是在IDE中给出警告:“您正在修改一个在过去30天被15个程序员修改过的Java文件,历史数据表明,您有95%的概率会产生Bug”。



PART 4

对于大规模软件开发项目来说,持续测试是一个常见的瓶颈。论文为缓解这个瓶颈提出了有启发意义的方向。论文发现,只有一小部分提交会产生Bug,也只有一小部分测试用例能够真正发现Bug。这意味着,尽管持续测试的需求很高,但其中真正有效的、或者说高优先级的需求只有很小的比例。如果对于每个CL我们能够提前预测其是否会产生Bug,那么我们就可以基于预测结果来调整测试调度策略:将测试资源优先分配给那些有较高可能性产生Bug的CL,从而给对应开发人员更快的反馈。为了实现这种预测功能,我们需要构建模型。特征工程是模型构建的基础。论文发现的几种相关性,例如开发人员信息、源文件类型、修改频率等与目标CL的关联性,可以认为是特征工程的一部分,为进一步利用机器学习技术构建预测模型奠定了一定的基础。这意味着,一直以来困扰我们已久的持续测试瓶颈问题,未来有可能被先进的数据分析机器学习技术迎刃而解


一直以来,测试的价值常常在于帮助开发人员发现Bug。持续测试的出现,也是为了更快、更早地发现Bug。论文的另一个启发意义在于:通过对大规模持续测试数据进行挖掘,我们完全有可能提炼出可以帮助开发人员预防Bug的建议。通过将这些建议集成到程序开发环境中,开发人员能够在编写代码的同时,就实时地收到来自测试的、有价值的反馈,从而减少Bug的产生、并且提高开发效率。从“治病”到“治未病”,从发现Bug到预防Bug,论文指引了软件测试的一个重要方向。


由衷地感谢这篇论文!它让我们认识到:自动化测试的边界正在拓展,自动化测试的发展永无止境!


分类:
代码人生
标签:
分类:
代码人生
标签:
收藏成功!
已添加到「」, 点击更改