你的单元测试有多稳定?提升自动测试质量的最佳实践
2014-03-21 17:28
288 查看
本文由 ImportNew - 陈
晓舜 翻译自 compuware。欢迎加入Java小组。转载请参见文章末尾的要求。
我们超过10K的单元测试大部分都是用JAVA的JUnit编写,并且用gradle自动构建工具运行。当我们添加越多的测试用例,就越频繁地遇到单元测试执行不稳定的问题。新添加的测试代码影响了现存的测试的执行。我们的”失败测试“(failed test)标准在它开始增加前一直表现地很不错。显然我们应该去抱怨那些糟糕的程序代码。但经过仔细的分析,我们发现造成不稳定的测试结果的真正原因。大多数由新测试造成的问题都是由于那些测试用例对测试环境作了一些不利的影响,因此也影响了其他测试的执行。
在这篇文章中,我会展示我们是怎么找出这些特定的失败测试的根因,并且由此得出的对测试环境友好的单元测试设计的最佳实践。
上个星期,我们发现一组测试没有毫无预兆就失败了。我自愿参加分析。
一组验证ThreadPool实现的重要且必须行为的单元测试。特别是,定时任务必须在特定异常抛出后继续运行。为了测试它,必须要有另外一个定时任务,抛出特定的异常。然后测试会在第一次因为异常失败后等待第二次执行。
在某些机器上运行这些测试,它们会在重新运行时超时。尽管有各种各样的异常处理器可以记录异常,但没有任何输出。只有这样一条消息输出:
然后,在Eclipse中运行时,这种情况永远都不可能重现,它只会出现在我们使用gradle来跑这些test的时候。
下一步:我配置gradle打开调试端口,然后使用eclipse连接以确定原因。这才发现NullPointerException是在gradle的某处代码中抛出。我下载了源代码,发现System.getProperty(“line.separator”)返回null,并且被取消引用。
有了这些信息,我检查代码,并且迅速发现另外一个校验不同平台上的字符串格式的测试在修改line.separtor属性时有不良影响。在测试完成后调用的System.clearProperty(“line.separator”),它无意中把该属性设置为null。在这种情况下,当下个测试运行时,由于它使用System.setOut重定向到控制台输出,日志信息会被写出到控制台,gradle代码会被调用。这种顺序的调用会导致gradle在获取line.separator属性值是抛出NullPointerException。注意,执行的顺序很重要,因此在ThreadPool测试时只是偶尔会出现。
通常一系列的处理器都可以捕获到它,但由于它们也写出到控制台,当它们遇到NullPointerException时,会向上抛出。
这个错误的快速的解决方式是去除clearProperty这个调用,而换用上一个line.separator属性的值。但我们是否可以做到更好呢?
上面的部分错误是本来并且应该由测试自动化去处理的,JVM通常会重用同一个项目内的对象。因此理想情况下的单元测试应该对测试环境没有副作用,可以避免类似的错误。
测试环境包括各种可以影响之后执行的测试的资源,如在文件系统中创建文件或者上面的情况,修改java系统属性。对于文件类的,Junit提供了TemporaryFolder规则用于创建临时文件,所以我们这里开发了一个属性的类似机制。
解决方案:单元测试中恰当使用系统属性
通常情况下,假设我们要开发一个测试用例,需要设置Java系统变量为一个特定值,可能会如下:
Test.java
.
但当其他在其之后运行的测试都运行在同一个JVM下,这个属性值还是会保留被设的值,并且会影响在后面运行的测试用例行为。所以,让我们来改进一下:
Test.java
看起来挺不错吧,不是吗?但还是存在一个问题:如果属性值没有设置,在setup中的setProperty返回null,而teardown会抛出一个NullPointerException。所以正确的使系统属性维持不变的单元测试应该是:
Test.java
这会有点啰嗦..基于Java 7的try-with-resources 语句的帮助类实现了同样的功能:
Test.java
or also:
或者:
Test.java
当然,最好还是直接实现Junit规则,在这种情况下你不需要写@After。例如:
Test.java
属性在@BeforeClass中进行设置,但会在@AfterClass中恢复成原始的状态。
结论:无副作用的测试可以提升自动化测试质量
要避免由于执行顺序引起的测试错误,单元测试必须做到没有副作用。现实世界的例子中证明这个话题的关联性。更深入这个练习,我们开发了一些用于系统属性的帮助类。相同的原理同样适用于其他环境对象,例如线程属性,Java安全管理器和类似ScopedProperty和ScopedPropertyRule之类的类,这些都可以很容易的实现。当我们在不断努力提升自动测试测试质量时,我对其他最佳实践保持兴趣。给我们留言,说一下你在开发团队里面的工作。(感谢Reinhold Füreder对Junit规则的引入!)
ScopedProperty.java
这里的ScopedProperty实现了AutoCloseable来完成try-with-ressouces语句
ScopedPropertyRule.java
这个类使用了ExternalResource来实现Junit测试规则
原文链接: compuware 翻译: ImportNew.com - 陈
晓舜
译文链接: http://www.importnew.com/10312.html
[ 转载请保留原文出处、译者和译文链接。]
晓舜 翻译自 compuware。欢迎加入Java小组。转载请参见文章末尾的要求。
我们超过10K的单元测试大部分都是用JAVA的JUnit编写,并且用gradle自动构建工具运行。当我们添加越多的测试用例,就越频繁地遇到单元测试执行不稳定的问题。新添加的测试代码影响了现存的测试的执行。我们的”失败测试“(failed test)标准在它开始增加前一直表现地很不错。显然我们应该去抱怨那些糟糕的程序代码。但经过仔细的分析,我们发现造成不稳定的测试结果的真正原因。大多数由新测试造成的问题都是由于那些测试用例对测试环境作了一些不利的影响,因此也影响了其他测试的执行。
在这篇文章中,我会展示我们是怎么找出这些特定的失败测试的根因,并且由此得出的对测试环境友好的单元测试设计的最佳实践。
这一切都因为缺少单元测试时间
上个星期,我们发现一组测试没有毫无预兆就失败了。我自愿参加分析。一组验证ThreadPool实现的重要且必须行为的单元测试。特别是,定时任务必须在特定异常抛出后继续运行。为了测试它,必须要有另外一个定时任务,抛出特定的异常。然后测试会在第一次因为异常失败后等待第二次执行。
在某些机器上运行这些测试,它们会在重新运行时超时。尽管有各种各样的异常处理器可以记录异常,但没有任何输出。只有这样一条消息输出:
下一步:我配置gradle打开调试端口,然后使用eclipse连接以确定原因。这才发现NullPointerException是在gradle的某处代码中抛出。我下载了源代码,发现System.getProperty(“line.separator”)返回null,并且被取消引用。
有了这些信息,我检查代码,并且迅速发现另外一个校验不同平台上的字符串格式的测试在修改line.separtor属性时有不良影响。在测试完成后调用的System.clearProperty(“line.separator”),它无意中把该属性设置为null。在这种情况下,当下个测试运行时,由于它使用System.setOut重定向到控制台输出,日志信息会被写出到控制台,gradle代码会被调用。这种顺序的调用会导致gradle在获取line.separator属性值是抛出NullPointerException。注意,执行的顺序很重要,因此在ThreadPool测试时只是偶尔会出现。
通常一系列的处理器都可以捕获到它,但由于它们也写出到控制台,当它们遇到NullPointerException时,会向上抛出。
这个错误的快速的解决方式是去除clearProperty这个调用,而换用上一个line.separator属性的值。但我们是否可以做到更好呢?
单元测试的副作用
上面的部分错误是本来并且应该由测试自动化去处理的,JVM通常会重用同一个项目内的对象。因此理想情况下的单元测试应该对测试环境没有副作用,可以避免类似的错误。测试环境包括各种可以影响之后执行的测试的资源,如在文件系统中创建文件或者上面的情况,修改java系统属性。对于文件类的,Junit提供了TemporaryFolder规则用于创建临时文件,所以我们这里开发了一个属性的类似机制。
解决方案:单元测试中恰当使用系统属性
通常情况下,假设我们要开发一个测试用例,需要设置Java系统变量为一个特定值,可能会如下:
Test.java
但当其他在其之后运行的测试都运行在同一个JVM下,这个属性值还是会保留被设的值,并且会影响在后面运行的测试用例行为。所以,让我们来改进一下:
Test.java
Test.java
Test.java
或者:
Test.java
Test.java
结论:无副作用的测试可以提升自动化测试质量
要避免由于执行顺序引起的测试错误,单元测试必须做到没有副作用。现实世界的例子中证明这个话题的关联性。更深入这个练习,我们开发了一些用于系统属性的帮助类。相同的原理同样适用于其他环境对象,例如线程属性,Java安全管理器和类似ScopedProperty和ScopedPropertyRule之类的类,这些都可以很容易的实现。当我们在不断努力提升自动测试测试质量时,我对其他最佳实践保持兴趣。给我们留言,说一下你在开发团队里面的工作。(感谢Reinhold Füreder对Junit规则的引入!)
附录
ScopedProperty.javaScopedPropertyRule.java
原文链接: compuware 翻译: ImportNew.com - 陈
晓舜
译文链接: http://www.importnew.com/10312.html
[ 转载请保留原文出处、译者和译文链接。]
相关文章推荐
- 你的单元测试有多稳定?提升自动测试质量的最佳实践
- 你的单元测试有多稳定?提升自动测试质量的最佳实践
- JQuery最佳实践—-看完绝对提升你的代码质量
- JQuery最佳实践—-看完绝对提升你的代码质量
- android单元测试最佳实践一:android studio测试环境搭建
- JavaScript 最佳实践:帮你提升代码质量
- JavaScript 最佳实践:帮你提升代码质量
- 移动App測试实战:顶级互联网企业软件測试和质量提升最佳实践
- 系统测试——代码质量检查、单元测试、性能测试、自动构建、项目管理
- JavaScript 最佳实践:帮你提升代码质量
- angular单元测试与自动化UI测试实践
- Istio技术与实践03:最佳实践之sidecar自动注入
- gulp最佳实践(包含js,css,html预编译,合并,压缩,浏览器自动刷新)
- 浅析如何提升软件测试质量
- 代码质量(权威精选植根于开发实践的最佳读物)
- ActiveX控件打包成Cab置于网页中自动下载安装 (收藏未实践测试)
- 敏捷测试(三)从一个实例详解敏捷测试的最佳实践
- Android UI自动化测试最佳实践
- (翻译)测试工具自动化的最佳实践