自动化工具 单元测试自动生成工具 evosuite 尝鲜

陈恒捷 for PPmoney · 2018年12月04日 · 最后由 陈恒捷 回复于 2020年03月08日 · 3997 次阅读

偶尔在群里看到卡斯提到 evosuite 这个工具,随手查了下,竟然可以自动生成单元测试,而且看官方视频还不是太复杂,赶紧尝鲜一下。

介绍

直接摘抄网上的:

EvoSuite 是由 Sheffield 等大学联合开发的一种开源工具,用于自动生成测试用例集,生成的测试用例均符合 Junit 的标准,可直接在 Junit 中运行。

通过使用此自动测试工具能够在保证代码覆盖率的前提下极大地提高测试人员的开发效率。但是只能辅助测试,并不能完全取代人工,测试用例的正确与否还需人工判断。

核心功能

  • Generation of JUnit 4 tests for the selected classes
    生成指定类的 Junit 4 测试用例

  • Optimization of different coverage criteria, like lines, branches, outputs and mutation testing
    通过不同的覆盖指标调整生成的用例,如行覆盖率、分支覆盖率、输出及变异测试(mutation testing)

  • Tests are minimized: only the ones contributing to achieve coverage are retained
    测试最小化,只有能贡献覆盖指标的用例才会被保留下来

  • Generation of JUnit asserts to capture the current behavior of the tested classes
    生成 Junit 断言来检验被测试的类的行为

  • Tests run in a sandbox to prevent potentially dangerous operations
    测试被运行在一个沙盒中,避免潜在的危险行为

  • Virtual file system
    虚拟文件系统

  • Virtual network
    虚拟网络

使用

官方提供了包括命令行工具eclipse 插件idea 插件maven 插件 在内的数种运行方式。

其中 eclipse 插件的可以参照 自动化单元测试工具 EvoSuite 的简单使用

四种都大致看了下,其中 maven 插件的方式最为简单,所以选取它来尝鲜。

接下来的流程主要参考官方的 http://www.evosuite.org/documentation/tutorial-part-2/ ,时间关系简略了一些解释说明,有兴趣的同学欢迎阅读官方原文了解。

准备工作

既然是尝鲜,当然不能直接用官方的项目啦,找了个自己以前找到的开源 java 项目:https://github.com/chenhengjie123/JavaSpringMvcBlog

下一步,按照官方指示,调整下 junit 版本

<dependencies>
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
  </dependency>
</dependencies>

生成测试用例

添加 evosuite 插件

<plugins>
      <plugin>
        <groupId>org.evosuite.plugins</groupId>
        <artifactId>evosuite-maven-plugin</artifactId>
        <version>1.0.6</version>
      </plugin>
...

(可选)官方提到,因为 maven 中央仓库没有收录这个插件,所以要加上 evosuite 自己的仓库地址。不过经过搜索,中央仓库现在已经有了,所以这步应该可以忽略。如果大家下载不了,可以把下面的配置也加到 pom 里面添加 evosuite 的仓库

<pluginRepositories>
  <pluginRepository>
    <id>EvoSuite</id>
    <name>EvoSuite Repository</name>
    <url>http://www.evosuite.org/m2</url>
  </pluginRepository>
</pluginRepositories>

为了执行用例,需要加入 evosuite 的运行依赖:

<dependency>
    <groupId>org.evosuite</groupId>
    <artifactId>evosuite-standalone-runtime</artifactId>
    <version>1.0.6</version>
    <scope>test</scope>
</dependency>

开始生成,只需要执行这个命令

mvn  evosuite:generate

为了加快速度一步到位,我加了不少参数:

mvn compile -DmemoryInMB=2000 -Dcores=2 -Dcuts=alexp.blog.service.PostServiceImpl -DtargetFolder=src/test/java/evosuite evosuite:generate evosuite:export

简单说明下:

  • compile 表示编译。evosuite 是基于编译后的 .class 文件生成用例的,所以需要先编译。
  • -DmemoryInMB=2000 表示使用 2000MB 的内存
  • -Dcores=2 表示用 2 个 cpu 来并行加快生成速度
  • -Dcuts=alexp.blog.service.PostServiceImpl 表示只针对 alexp.blog.service.PostServiceImpl 这个类生成用例。多个用例可以用英文逗号分隔
  • -DtargetFolder=src/test/java/evosuite 表示生成的用例放到 src/test/java/evosuite
  • evosuite:generate 表示执行生成用例
  • evosuite:export 表示导出用例到 targetFolder 的值所在的目录中

执行完毕后,在 src/test/evosuite 下会增加一个 alexp/blog/service 文件夹,里面存放生成的测试文件:

src/test/java/evosuite
└── alexp
    └── blog
        └── service
            ├── PostServiceImpl_ESTest.java
            └── PostServiceImpl_ESTest_scaffolding.java

3 directories, 2 files

PostServiceImpl_ESTest.java:测试用例文件
PostServiceImpl_ESTest_scaffolding.java:用例基类,用于在开始测试前初始化 evosuite 的沙盒机制

实际生成的部分代码会有编译错误(mock 的类型和 model 对不上),先通过注释跳过会编译出错的行,然后直接在 idea 执行 PostServiceImpl_ESTest.java ,并统计覆盖率:

看起来覆盖率还挺高。当然,覆盖率并不代表全部,更重要的是测的内容。

摘抄一条生成的单测用例:

对应的被测函数:

可以看到

  • 大量采用了 mockito 来 mock 掉外部对象。
  • 通过 mock 掉 findByHiddenIs 方法固定返回 null ,进而测试 getPostsList 是否也返回 null ,逻辑上没错,结果可以做到很稳定。

但可惜:

  • 没有校验正常逻辑(看了下整个类,2 个用例校验 null ,一个校验非法参数,唯独缺少正常场景)

不过总的来说,作为异常场景的补充,确实能省很多力。

完整测试代码和被测代码,可以查看:

被测代码:
https://github.com/chenhengjie123/JavaSpringMvcBlog/blob/master/src/main/java/alexp/blog/service/PostServiceImpl.java
生成的测试代码:
https://github.com/chenhengjie123/JavaSpringMvcBlog/blob/master/src/test/java/evosuite/alexp/blog/service/PostServiceImpl_ESTest.java
作者自己写的测试代码(场景不全,但覆盖了正常场景):
https://github.com/chenhengjie123/JavaSpringMvcBlog/blob/master/src/test/java/alexp/blog/service/PostServiceTest.java

结论

工具使用简单,生成的用例确实有效,推荐试用。

但比较适合用于生成一些异常场景的用例,省不少力(当然还得人工 review 下)。正常场景还是得靠人。

时间有限,体验不是十分深入,不排除是使用姿势不正确导致上述效果。大家如果有建议或者不认可的地方,欢迎提出,一起交流~

参考文档

官方网站:http://www.evosuite.org
csdn 上《自动化单元测试工具 EvoSuite 的简单使用》:https://www.cnblogs.com/fang888/p/9008280.html

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 21 条回复 时间 点赞

不错不错,找时间玩一下,今天刚好还在看邱化峰大佬在某个峰会的分享说了这个 Java 自动生成单测的 topic

不知道生成的 @ TestCase 可用率怎么样?有效性如何?生成后需要人干预的程度高吗?

哎哟,不错哦~刚试了下,挺好玩

4楼 已删除

感谢大佬分享。准备尝鲜下

好像 private 方法不能生成,是这样的么?,还是操作姿势不对?😄

testly 回复

可用率还可以,80% 左右吧。有效性个人感觉还不错,确实是要覆盖的场景
人工干预还是要的,用例还是要 review 下和人工补充一下

主要是意识,大家体会到甩代码给测试的爽快感后就不想搞测试了

佳佳 回复

没测试过,不大清楚。不过我理解一般不会对 private 方法做单测?

陈恒捷 回复

貌似 APP 工程用不了

16 年的时候研究过,还提过几个 issue,毕竟是学术工具,实践来说,不如 Jtest 生成的靠谱,可惜 Jtest 商业版收费。

楼主,有个问题想问你,我按照你的配置来做的,但是并没有生成对应的测试代码。我看显示的是 WARN: failed to generate tests for 5 classes out of 5,去看了错误日志,提示我下面的错误:
java.lang.IllegalArgumentException: null
at org.evosuite.shaded.org.objectweb.asm.ClassReader.(Unknown Source)
at org.evosuite.shaded.org.objectweb.asm.ClassReader.(Unknown Source)
at org.evosuite.shaded.org.objectweb.asm.ClassReader.(Unknown Source)
at org.evosuite.setup.InheritanceTreeGenerator.analyzeClassStream(InheritanceTreeGenerator.java:238)
at org.evosuite.setup.InheritanceTreeGenerator.createFromClassPath(InheritanceTreeGenerator.java:101)
at org.evosuite.setup.DependencyAnalysis.initInheritanceTree(DependencyAnalysis.java:77)
at org.evosuite.setup.DependencyAnalysis.analyzeClass(DependencyAnalysis.java:131)
at org.evosuite.TestSuiteGenerator.initializeTargetClass(TestSuiteGenerator.java:110)
at org.evosuite.TestSuiteGenerator.generateTestSuite(TestSuiteGenerator.java:130)
at org.evosuite.rmi.service.ClientNodeImpl$1.run(ClientNodeImpl.java:145)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)

请问有遇到过吗

android 工程支持吗?

husc 回复

木有遇到,有没有更详细的信息?

ray 回复

理论上只要是 java 都支持,但不确定 android 会不会有坑,建议可以自己试下。

陈恒捷 回复

试了 android 可以支持

17楼 已删除

请问下是如何支持安卓的,我在 java 环境下是 ok 的,安卓上无法生成用例

ray 回复

请问下是如何支持安卓的,我在 java 环境下是 ok 的,安卓上无法生成用例..

iOS 有类似的框架吗?

usernameli 回复

可以生成,但覆盖率不高,涉及 android sdk 的文件无法生成

pengxuyuan 回复

没有特意去了解过,不知道 swift 或者 oc 有没有类似工具。

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册