偶尔在群里看到卡斯提到 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 ,并统计覆盖率:
看起来覆盖率还挺高。当然,覆盖率并不代表全部,更重要的是测的内容。
摘抄一条生成的单测用例:
对应的被测函数:
可以看到
findByHiddenIs
方法固定返回 null ,进而测试 getPostsList
是否也返回 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