美文网首页
饿了么测试开发架构师邱化峰谈人工智能在Java单元测试中的应用

饿了么测试开发架构师邱化峰谈人工智能在Java单元测试中的应用

作者: caf0410d2620 | 来源:发表于2018-11-16 18:48 被阅读72次

    邱化峰:大家下午好,首先感谢给我们这样的机会来学习,关于测试方面的一些基础的东西,我今天下午讲的是人工智能在Java单元测试中的应用。大家不要以为人工智能是一个很高大上的东西,我们的业务,或者是业务线上的应用(使用了人工智能就很高大上),其实不是这样的。其实人工智能已经深入到工作过程中,其实对我们来说使用AI并不是一件很困难的事。使用人工智能最简单的就是如何把一些东西转变到一些函数过程中,通过函数得出一个数据结果来。没见得今天的(业务)算法有多大高上的东西,利用人工智能提升我们的工作效率?不是这样的。(不是所有的人工智能都能提升工作效率,现在使用不好的其实是在降低工作效率)

    我从三个部分进行分享:工具的介绍,原理方法,以及使用这些工具做了哪些应用场景。工具介绍(我目前搜到的已知的)通过人工智能生成单元测试应用的方法有下面三种,实际上这个PPT是一个月之前编写的,现在有一种新的技术,就是变成了四种,第一种是基于搜索式的方式,第二种是基于随机测试方法,第三种是静态分析,上午的时候前面有几位专家也提到了,现在静态分析在我们质量保障过程中变得越来越重要了。第四种是基于文档的生成单元测试的。(https://github.com/albertogoffi/toradocu 通过java的文档注释自动生成断言)大家可以看到,每一种方法下面对应的都有一些开源的工具,大家是可以直接使用这些开源工具直接运用到项目中的。

    这个是使用人工智能的技术来生成单元测试用例的项目,以及项目后面对应的代码和实践,这些都是公开的,大家自己可以把这些东西下载下来自己实践一下。因为有了这么多的工具,我们目的是利用工具提高我们的效率,大家可以针对某种工具去实践一下,看看能达到什么样的效果,是不是工具生成单元测试能够帮助我们发现bug或者什么。我第一个推荐的就是第一个工具(EVOSUITE),因为它是动态维护的,还有第三个(Randoop),通过文档生成自动单元测试工具的没有列在PPT上,toradocu这个工具没列在PPT上,大家可以使用这些工具来进行实践。

    大家也知道NLP 自然语言处理技术也非常成熟了,国外一个研究机构已经在做了(德意志意大利语大学 toradocu),通过程序员写的代码注释,自动生成单元测试,通过这个注释生成单元语言用例,这个比刚才介绍的这几种工具有一个更明显的优势,它的优势就是只要这个人写的(代码注释)这个东西逻辑是正常的,我通过代码注释处理转化成测试用例的话,测试的覆盖度和测试用例适用性会比前面的高。也许大家会想,我现在业务上很忙了,没有时间去写代码注释,或者参数注视,还没按照业务要求完成功能,不用去写(注释)在这个时候。这个问题也困惑了我很久,后来我发现,其实现在通过人工智能,还有一些AI的算法,现在有另外一家机构,他们在研究的是什么呢? 通过语法树的输入自动的扫描代码的实现,自动的写出代码注释。这样的话, 通过文档生成单元测试用例,如果开发不写代码你可以通过扫描代码写注释,通过自动写代码注释,自动添加代码注释,自动的补文档,这样就能达到一个我们期望的闭环,可以直接串起来。从两个方向,第一个是我没有注释的情况下通过人工智能去生成(注释https://github.com/liang2024086/code_comment_generation),第二个是利用文档注释自动生成单元测试用例。

    其实搜索式的方法和现状,我要自动的生成单元测试的用例,要去断言我面临哪些问题怎么解决这些个问题?给你一段程序自动生成单元测试用例的时候,里面是不是每一个分支都是可达的,我是不是也能通过生成单元测试用例去发现它。还有就是我使用的算法,生成单元测试用例的算法,我这些算法里边哪个最优,这其实都是需要,应该这么讲,针对不同的项目,我在使用工具的时候,我要选择不同的生成用例的算法才会达到一个比较好的效果。然后,时间和成本大家都是比较关注的,通过人工智能的工具生成单元测试用例的时候,也需要考虑时间和成本的。

    我们具体讲一下,我怎么做到,或者说这些工具它怎么做到的,自动生成单元测试用例,怎么达到断言效果的。大家可以看到,无论是做自动化的生成用例,还是工具这块,包括现在,应该说从去年开始,现在大家都在关注覆盖率。其实自动生成单元测试用例就是判断(覆盖率),跟覆盖率有一个关联关系,你可以选择工具是基于行覆盖目标,还是分支覆盖,还是说直接调用方法直接来,还有弱突变测试,还有输出覆盖和异常覆盖。异常覆盖对自动生成单元测试用例是比较容易实现,我可以利用Mock(工具)一些东西直接自动生成单元测试用例覆盖的(模拟异常)。弱突变测试(weak mutation testing)是一个基础,如果没有这个你无法确认测试数据是什么样的,通过弱突变测试我们找到最优秀的测试数据,以及怎么来断言,是这样的。(想了解突变测试是如何来找到更加精准的测试数据和如何来辅助做更准确的断言的请参考 http://www.evosuite.org/wp-content/papercite-data/pdf/tse12_mutation.pdf)

    这几个工具里边都是用了这几个函数去做生成单元测试用例的条件。比如说我生成自动用例用函数表达出来,只有把它表达出来之后我生成单元测试用例再去执行,才能看到具体的结果,这个测试用例的函数希望表达式大家稍微了解一下知道怎么回事就行了。实际过程中使用工具的时候,你不会看这些函数是什么样的,你只确定生成用例的结果是不是我需要的就行了。

    我拿第一个工具,具体的实现测试用例的方法去做一个具体的讲解。第一个工具实践的时候,有三种方法实践自动用例生成的。第一个方法是同一个时刻只有一个目标这个目标只针对异常,或者只针对其他的,选择这个策略之后就跑单元测试用例的时候,是基于自觉码驱动的,我通过这个可以拿到所有的变量,所有的常量,把变量,常量,表达式转化为抽象语法术里边的某个节点,通过这些节点就可以进行自动生成单元测试用例。第二个方法是全套件测试,所有的都可以去判断一下它的情况。第三种是已经被覆盖掉的,怎么增加一些新的覆盖。

    我们可以看一下这个效果对比图,无论你自动生成单元测试用例,还是执行过程的用例,我主要关注三个指标,一个是行覆盖,一个是分支覆盖,还有一个是变异的结果是什么样的。大家可以看到,这个指标不仅仅是说我自动化的工具测试工具需要考查的一个指标,大家普遍作为一个质量衡量的一个标准。大家看左边是行,右边是时间,随着时间不断的增长,测试用例和断言用例也会不断的增加。为什么会出现这种情况,我后面会做一下解释。

    这个也是生成分支覆盖行覆盖一个结果的展示,大家看到针对每种不同的结果和算法,最终产生的分支覆盖目标结果也是不一样的。比如说我通过程序自动生成来的有很多不确定性,我不确定我执行哪个方法,按照哪个方法进行覆盖。如果你的程序偏向于算法级别的,你可能选择其中某一个算法,你的程序是偏向于其他的话就需要选择其他的测试方法。

    那这个工具呢,目前有一些不支持的情况,但是这个不支持的情况,目前对我们饿了么来讲是有一些影响的。我们基础框架基本用了HttpOK(EvoSutie),这个有些是不支持的,在利用它的时候会有很多的错误。我第一次用的时候花了7个小时,生成了600多个类的单元测试用例,我利用人工智能自动生成单元测试用例效率要比人工高很多,生成单元测试断言也比人工高很多,但是有一个缺点,单元测试用例的可读性不是很高。

    我给大家看一下,我使用这个工具对于测试也好,对于开发也好,是不是真正能够帮助到大家。我们可以从四个指标上去衡量,第一个指标,覆盖率,自动生成工具,通过工具自动生成的单元测试的覆盖率比人工高很多,基本上自动单元生成的测试用例覆盖率在80%以上,有的还会更多。第二个就是,其实我们可以用任何工具做测试也好,我们使用这个工具是不是真正能够发现bug,其实通过自动生成单元测试用例的工具,工具是不是也会发现bug。还有就是对其他的形成,它对生成测试用例的时候,这时候会产生一些。假如说有某种数据的算法,如果采用了不正确的生成用例的方式,可能生成的测试用例的覆盖率就不会太高,是这样的。

    还有一个就是,生成的数据,其实我们并不能保证所有的生成的测试数据,它都能够达到一个较高的水平。我们用突变测试的根来做,稍微改变一下运行,它就可以直接确定,我用了最小的数据生成了更多的高质量的测试数据。那着重讲一下这个图,为什么讲这个呢,这个是自动单元生成工具衡量的重要指标,是否能够发现真实的bug。大家看左边这个(defects4j),这个是作为现在不管是突变测试也好,还是其他的测试理论也好,我怎么去衡量我的这个工具能否达到一个好的效果,都是以这个作为一个比较,它实际上是大量的类库,这有两个版本,一个是产生bug的版本是什么,以及修复bug的版本是什么样,通过两个版本的比较得到一个实际的结果。大家可以看到,无论国际上的产生自动生成的测试工具还是什么,都是以这个作为一个基础的库,这个是可以扩充的。大家可能会问,比如说这个发现底层的一些,那我业务上是不是也可以,业务上也可以,但是你必须放在这个库里边,我们才利用工具帮助你发现bug。大家其实可以看到这里边使用了三种工具,其他的工具,我们也没有太多的时间去实践一下,所以只能把其中的三种工具做了简单的对比。(defects4j收集的是所有可以重新的Bug,它同时为更高级的软件测试技术提供了基础的Bug库,大家可以打开用https://scholar.google.com/scholar?start=60&q=defects4j&hl=en&as_sdt=0,5 就可以有哪些新的理论和新的方法用它作为基石数据)

    我们可以发现,通过使用这个库,我们使用的这几种工具,基础的库里边是有357个真实的bug,(最新版本已经更新到395个真实的Bug)使用这个工具,我们是发现了55.7%的真实的Bug,也就是说,我们使用这个工具的确是发现bug的,但是只能达到50%-60%的效果,还有40%的bug是不能通过使用这个工具自动生成的单元的case发现的。这个可能是影响这个工具在我们公司推广的效果,如果你不能发现bug的数量达到80%或者90%,你现在只是相当于60%,所以效果会有,但是说如果想在这个公司推广使用的话会存在一些挑战,所以我建议大家,去使用这个工具,去发现之后然后去提交不断的去改进。

    我们还要看一下,通过单元测试生成的工具里边,我到底发现了什么样类型的bug,以及什么类型的bug我们目前发现不了。这个简单的对工具case进行了分类,主要是三种bug,普遍缺陷,较严重的缺陷和临界缺陷。大家看时间对比,随着时间的增加我们发现的bug数量也在增加。大家想想为什么会发现这样的情况,因为这个是不断的判断,不断的通过覆盖率,通过这个不断的进行搜索,通过不断的增加分支产生的case也会不断的增加,随着产生case不断的增加我发现bug的数量也是增长的。但是这个时间不是设的越长越好,因为它是把所有的生成的case放在内存里边的,如果你设定时间台长会把机器的内存消耗近,不建议大家把时间设的太长,这个时间设置也是有一些方法可循的。

    然后我们具体看一下,这三种类型的bug都是指哪三种。第一个是NPE,我通过单元测试的工具生成,如果让开发也好,测试也好模拟异常也比较困难,但是通过这个工具就可以很容易的处理这些异常。第二种缺陷,我们把很多数据(或者是配置)存在介质中,或者存在外部,例如:数据库连接配置,我可以控制我连接的数据库是哪个IP,哪个端口,或者做动态配置,或者其它的东西的配置或者参数,就会有很多的东西是写在了程序的外部中。这个时候使用通过外部存储配置或者参数的的程序,在通过工具生成的单元测试用例,发现bug的概率就会降低,因为它不能正确的载入内存并被驱动搜索发现。

    然后再看下面的这个,下面的这个就比较复杂了,大家看这个里边又加了很多的条件,其实就是说工具对于这个生成的东西,还是有一些缺陷,也不能说是缺陷,它对这个东西不具有优势。我生成的测试数据,中间可能会有一些重复的,我怎么去重,这个时候第一个除了用了正交实验测试去重,之外另外通过覆盖率去重,在单位时间内我生成自动化单元测试用例的质量就降低了。(正交试验,用部分试验来代替全面试验,正交试验能够大幅度的减少用例数,而不降低覆盖度)

    那这个工具开发出来不仅仅只是针对测试人员用的,我们想推广到开发那边使用。这个是通过网上的一个调查问卷得到的一个结果,也就是说,其实在使用工具过程中,大家可以发现,如果把这个工具分成五个等级,哪个方面是现在目前大家比较愿意接受的。大家可以看到第一是十分困难的,其实现在单元生成工具,通过人工智能最后生成单元测试,是比较难以接受的,我生成单元测试的可读性不是很高。自动生成单元测试用例不高呢,有一个通过抽象语法树来生成文档注释的,如果把这个移用过来,就会得到一个很大的改善。(Code-RNN,详情请参考https://github.com/sriniiyer/codenn )

    还有一个是解决Java里边比较困难的,就是解决一个完整性的问题。我们经常出现版本不一致,或者差一个层次,都有可能导致这个工具自动生成的时候就会出现很多的问题,是这样的。然后这个工具,我不仅仅是这个工具,包括目前我也知道的,前面的四种加上刚刚说的第五种,目前都需要目前解决以下几个问题,如何解决这个结果,如何通过工具来生成,这个也是需要一些新的算法和理论来去做的。第二个就是说,我的断言,其实对于这个的断言你说我增加了一个值,直接把这个值直接作为断言比较好,以哪个作为优先会比较好一些,比如List 类型,当我增加一个元素到List中,是使用List.size 作为断言,还是使用List. Length作为断言。因为是代码驱动的,所以有一个限制,这个限制导致一些类库是不能使用的,最后是测试用例的可读性,是这样的。

    前面的两部分我们讲了这个工具,介绍了有哪些工具,工具生成单元测试的一些应用案例。后面会讲一些,通过测试工具,在饿了么做了哪些应用,以及这些工具是不是真正让大家可以直接采用的。我希望今天大家听完这个分享之后,回去之后自己实验拿着现成的东西去做就行了,不会让你去有太多的问题,可以降低你使用人工智能的门槛。它提供了三个方法来让大家比较容易的使用,命令行方式,Eclipse插件方式和Maven,你不需要了解具体是怎么实现的自动生成单元测试用例的,断言是如何自动产生的。如果你在使用过程中发现bug需要去解决,或者发现哪些问题,你可以再去细读一下里边的原码什么的,可以进行持续提高。

    然后这个是使用工具,跟我们DRC,DRC是饿了么分布式多活,异地多活重要的组件,这个组件里边,我们通过它大概使用了三个小时,生成了1867个case。大家可以看到,这个标题里边有一个工具的结合,其实这个工具自动生成的用例,其实还是一个junit报告展示出来的,这个Junit展示出来的结果第一个不太友好,第二个我们希望能够看到这个结果,所以我们使用了一个开源的框架Allure,这个框架目前不仅仅是在我们单元测试用例的一个报告的方面还可以集中在其他语言任何语言的单元测试,接口测试,以及系统测试的报告都可以接到这里边来(Allure)。大家看它的期望结果和实际结果,都会自动的展示出来。你根据左边红色、蓝色、绿色、黄色去点,会把相应的case直接列出来。(详细的报告展示可以参考 https://github.com/allure-framework/allure2 )

    然后,我有了这个工具,有了自动化的报告,就能够把它跟我们的Jenkins去做集成,这个东西除了提供日常工具之外,还提供跟其他接口的对接,我可以通过Jenkins去做其他的工作。我们稍微做了一些改进,通过代码的diff 可以获取到代码变更的类,针对这些变更的类自动生成单元测试用例,我需要工具生成的类就会变得很少,之前已经生成过的可以回归到库里边,这样执行回归的过程中,你发现我通过这种方式可以做快速回归的方式也是可以达到的,我可以把所有的通过这个工具生成的测试用例使用起来 。

    然后这个规则识别的问题给列出来了,这个也是我们核心组件DRC里边实践的一个代码,这个代码很有意思,它是要做使用CPU的异常保护,大家看到这个规则是违反了我们编写代码规则的规范,它每次都需要在这个方法里边去调用一个方法,所以就是说,为什么要把这个单独列出来,如果你的业务场景里边本身实现的话,比如说有一些去违反我们正常编写程序代码规则和规范的时候就不要利用这个工具生成了,因为你生成出来之后也会成为开发之间挑战的东西,不会有利于提升我们的效率,需要大家在用工具生成单元测试用例测试完之后,一个比较理想的模式,不是工具生成之后就OK了,我们需要做的就是,我们需要针对失败的要进行分析,分析出具体原因之后再提出给开发,否则的话,开发就会说这个工具根本就不行。

    最后我要说一下,这个工具人工智能和人,到底人超越人工智能,还是人工智能超越人,实际上通过使用这个工具,把原来的质量衡量的三个指标,时间、质量成本拓展成五个方面。你会发现,时间质量和成本在自动生成单元测试用例和资源调用的时候我可以时间换空间,也可以空间换时间,可以用工具帮助人短时间形成更多的case,这个时候就会出现一些问题,我测试人员是不是就失业了,不是这样的,因为生成的case也好,生成的东西也好需要人来判断这个是不是真的bug,是不是真的符合业务场景,所以说一些工具它本身有一个认知,或者有一个判断,或者是一个规则提取的能力,我把它拓展为认知度,工具还需要有一个怎么样认知更多的东西,或者能够使用更多的策略方法来去生成更精准的case。还有一个就是我把工具自己能够自己,如果程序或者自动生成单元case的工具,自己把自己给迭代掉产生新的版本。

    提问:你好,我想问一下,因为人工智能我们是要经过训练的,训练也是有一个过程的,那在这个监督的过程中是不是有一个标的?

    邱化峰:其实我使用的是基于搜索方式,做了一个效果图的展示,其实在生成基于搜索式的方法,你的测试集也好,还是训练集也好,产生不同的数值,然后去看覆盖率,训练集得出的结果,而是通过代码被覆盖掉产生的效果。通过覆盖率,通过我的结果把这个正向的做加减乘除,通过这个判断是不是生成的用例是否有效。

    因为是生成单元测试的,我输入一个结果产生一个输出结果,中间的过程是通过一串代码,拿到这个数据之后作为一个集合体,通过集合体进行单测,实际上不存在哪一个作为训练机,哪一个作为样机,只是使用一个算法来算用例的,我们测利用人工智能实现一个框架,输入这样的数据得到什么样的结果,这两个是不同的概念。(在生成用例的过程中是有指标的,指标有多个,主要使用的指标有 变异的分数 ,覆盖率)

    提问:老师,您好,你这个预期结果是怎么获取的?

    邱化峰:预期结果里边,实现方式里边,已经决定了你实现的原理和方法。我在生成测试用例基础集的时候,我用抽象语法树(AST)表达出来,我是循环的,还是判断的,通过这个生成测试用例,根据规则里边的条件判断这个值作为断言的一个结果,是这样的。

    提问:你们现在尝试着做二次开发吗?

    邱化峰:我们已经在尝试去做二次开发和bug的修复。对于一个测试,或者有一些工具的人去做改进的话,目前确实有一些困难,但是你通过代码不断的发现,并没有我们想象中的那么复杂,但是想加入更多的算法还没有去做。

    提问:因为我们公司是比较单一的方法自动生成还可以,但是利用逻辑方法的时候生成就不是那么好用了。

    邱化峰:这个我理解,我利用这些工具,分别设了三四种不同类型的,试过了大数据人工智能推荐的试了DRC数据服务的,还有一个业务类型的,还有一个是什么服务。你利用工具自动生成单元测试用例的时候,你要考虑在那一层实现这个。你利用人工智能推荐算法的时候,是通过中间计算逻辑那块,大都的抽出来去做。这个工具因为不能识别出来程序是什么样的,或者你程序达到什么要的目标是不可以的,但是我们人为识别出来,那些数据做测试用例,这样就会达到一个比较好的效果。

    相关文章

      网友评论

          本文标题:饿了么测试开发架构师邱化峰谈人工智能在Java单元测试中的应用

          本文链接:https://www.haomeiwen.com/subject/nfqvfqtx.html