JUnit使用经验 <转载>
原作:Andy Schneider Richard Dallaway 等
编译:PMT 测试工作组
--------------------------------------------------------------------------------
译者注:
很多测试人员都有过编写测试框架的经历,JUnit的出现避免了其中的大量重复劳动。但如同其他的工具一样用得好和用得差的结果是截然不同的。我们编辑这样一个JUnit的系列希望能够帮助越来越多的JUnit使用者用好JUnit。我们也希望读者们能够把自己的一些经验所得和大家分享。
经验一、不要在测试用例的构造函数中做初始化
答案是重载测试用例的setUp()方法进行初始化。
经验二、不要假定测试用例中测试的执行次序
好的习惯是保持测试之间的独立性,使得它们在任何次序下执行的结果都是相同的。
经验三、测试要避免人工干预
经验二讲的是不同的测试要避免相关性,而经验三讲的其实就是测试要避免自相关。
经验四、在子类中调用父类的setUp() 和tearDown()
经验五、不要硬性规定数据文件的路径
经验六、把测试的代码和被测的代码放在同样的目录下
经验七、正确命名测试
经验八、书写测试时要考虑地区和国家设置
经验九、利用Junit 的自动异常处理书写简洁的测试代码
事实上在Junit 中使用try-catch 来捕获异常是没有必要的,Junit 会自动捕获异常。那些没有被捕获的异常就被当成错误处理。
经验十、充分利用Junit 的assert/fail 方法
assertSame()用来测试两个引用是否指向同一个对象
assertEquals()用来测试两个对象是否相等
经验十一、确保测试代码与时间无关
经验十二、使用文档生成器做测试文档
经验一、不要在测试用例的构造函数中做初始化
当我们需要增加一个测试时,我们要书写一个自己的测试用例,比如SomeTest。如果你喜欢在SomeTest的
构造函数中做有关的初始化工作,这可不是个好习惯。如下例:
public Class SomeTestclass SomeTest extends TestCase{
public SomeTest(String testName){
super(testName);
//初始化代码
}
}
一旦初始化代码产生异常,比如IllegalStateException,JUnit随之将产生一个AssertionFailedError,
并显示类似下面的出错信息:
junit.framework.AssertionFailedError:Cannotinstantiatetestcase:test1at
junit.framework.Assert.fail(Assert.java:143at
junit.framework.TestSuite$1.runTest(TestSuite.java:178at
junit.framework.TestCase.runBare(TestCase.java:129at
junit.framework.TestResult$1.protect(TestResult.java:100at
junit.framework.TestResult.runProtected(TestResult.java:117at
junit.framework.TestResult.run(TestResult.java:103at
junit.framework.TestCase.run(TestCase.java:120at
junit.framework.TestSuite.run(TestSuite.java,CompiledCodeat
junit.ui.TestRunner$12.run(TestRunner.java:429
这一大堆出错信息只会让人一头雾水,我们只知道JUnit无法实例化某个测试用例,到底出了什么问题,在
哪儿出错了呢?不知道!
那么好的做法是怎样呢?
答案是重载测试用例的setUp()方法进行初始化。当setUp()中的初始化代码产生异常时我们得到的
是类似下面的出错信息:
java.lang.IllegalStateException:Oopsatbp.DTC.setUp(DTC.java:34at
junit.framework.TestCase.runBare(TestCase.java:127at
junit.framework.TestResult$1.protect(TestResult.java:100at
junit.framework.TestResult.runProtected(TestResult.java:117at
junit.framework.TestResult.run(TestResult.java:103
...
显然这要清楚得多我们一下子就可以知道是在DTC.java 的第34 行产生了IllegalStateException
经验二、不要假定测试用例中测试的执行次序
我们知道在一个JUnit 的测试用例类中可以包含多个测试,每个测试其实就是一个method。在下面的例子
中有两个不同的测试,尽管testDoThisFirst()在位置上先于testDoThisSecond(),但我们不能就此假定
testDoThisFirst()会先执行。
public Class SomeTestCaseclass SomeTestCase extends TestCase{
public SomeTestCase(String testName){
super(testName);
}
public void testDoThisFirst(){
}
public void testDoThisSecond(){
}
}由于JUnit 内部使用一个Vector 来存储所有的test,因此在不同的操作系统和Java 虚拟机上,test 的执行
次序是不可预测的。
好的习惯是保持测试之间的独立性,使得它们在任何次序下执行的结果都是相同的。如果真得需要某些测试
按照特定的次序执行,我们可以借助addTest 来实现。如下例:
public static Testsuite(){
suite.addTest(new SomeTestCase(“testDoThisFirst”);
suite.addTest(new SomeTestCase(“testDoThisSecond”);
return suite;
}
这样我们可以确保JUnit先执行testDoThisFirst(),然后执行testDoThisSecond()。
经验三、测试要避免人工干预
如果某段测试代码需要人工干预,那至少有两个不良后果:一则不能被包括在自动测试中,比如夜间的回
归测试;二则不能被重复执行,例如数据删除的测试不能做完删除就万事大吉,比较好的做法是自动补上
删除掉的数据。经验二讲的是不同的测试要避免相关性,而经验三讲的其实就是测试要避免自相关。
经验四、在子类中调用父类的setUp() 和tearDown()让我们看一看下面的代码
public Class SomeTestCaseclass SomeTestCase extends AnotherTestCase {
// A connection to a database
private Database theDatabase;
public SomeTestCase (String testName) {
super (testName);
}
public void testFeatureX () {
}
public void setUp () {
// Clear out the database
theDatabase.clear ();
}
}你发现其中的错误了吗?setUp()应该调用super.setUp() 以确保AnotherTestCase 中定义的环境被初
始化了。当然这也有例外,就是基类可以处理任意的测试数据。
经验五、不要硬性规定数据文件的路径
我们经常需要从文件系统中读取测试数据,看下面的代码:
public void setUp () {
FileInputStream inp ("C:\TestData\dataSet1.dat";
}
这段代码需要把测试数据文件dataSet1.dat 放在C:TestData,这是有问题的。
第一,C 盘可能没有磁盘空间了测试人员不得不把数据文件放到其他路径;
第二,可能需要在其他操作系统比如Linux 上执行这一测试。
所以,一个较好的替代方案是
public void setUp () {
FileInputStream inp ("dataSet1.dat";
}
但事实上这样仍不是很好,因为这要求数据文件的路径和测试执行的路径必须是同一个,如果几个不同
的测试都这样的话,那要把这些测试集合起来执行就有些困难,我们不得不频繁的改变当前路径。
为了解决这个问题,我们可以使用Class.getResource()或者Class.getResourceAsStream(),这样我
们可以把数据文件放在这个Class 的某个相对路径上。
数据文件应该尽可能和源代码一起都放在配置管理系统上,但这样一来如果我们采用上面的Resource 机
制,我们就需要做一件工作,就是把数据文件从原来的位置-就是源代码的某个相对路径,拷贝到编译后
的位置,也就是class 文件的相应的相对路径。这其实并不复杂,因为从class 的package 就可以映射到
java文件的所在路径对于Linux或者Windows我们所要做的就是把package中的. 用
File.separatorChar 替代。
经验六、把测试的代码和被测的代码放在同样的目录下
当我们把测试代码和被测的代码放在同一目录下时,我们就可以在编译被测代码的同时编译测试代码,从
而确保两者是同步更新的。事实上当前的普遍做法,就是把单元测试视为Build 的一个环节。
经验七、正确命名测试
把测试用例命名为TestClassUnderTest,比如如果被测的Class 是MessageLog,那么测试用例就叫
TestMessageLog,这样做使得测试用例和被测的Class一一对应,而在测试用例中每个测试的method 就
可以命名为
testLoggingEmptyMessage()
testLoggingNullMessage()
testLoggingWarningMessage()
testLoggingErrorMessage()
同样是为了说清楚测试的是什么。正确的命名可以帮助测试代码的阅读者了解每个测试的目的。
经验八、书写测试时要考虑地区和国家设置
比如某个测试要使用日期,下面的代码就是创建日期对象的一种方法
Date date = DateFormat.getInstance ().parse ("dd/mm/yyyy";
但是如果运行上面测试代码的机器采用不同的地区国家设置,那么就会有问题。因此我们最好用下面的另
一种方法:
Calendar cal = Calendar.getInstance ();
Cal.set (yyyy, mm-1, dd);
Date date = Calendar.getTime ();
显然,第二种方法能够适应地区国家设置的变化。
经验九、利用Junit 的自动异常处理书写简洁的测试代码
很多Junit 的初学者经常会写出类似下面的这些代码
public void exampleTest () {
try {
// do some test
} catch (SomeApplicationException e) {
fail ("Caught SomeApplicationException exception";
}
}
事实上在Junit 中使用try-catch 来捕获异常是没有必要的,Junit 会自动捕获异常。那些没有被捕获的异常就
被当成错误处理。所以上面的代码很冗余,完全可以写成下面等效却简洁得多的代码:
public void exampleTest () throws SomeApplicationException {
// do some test
}
更少的测试代码也更容易读懂更容易维护。
经验十、充分利用Junit 的assert/fail 方法
Junit 有丰富而灵活的assert/fail 方法,如何用好这些方法也是大有讲究的。比如下面的写法就不大好
assert (creds == 3);
不如写成
assertEquals ("The number of credentials should be 3", 3, creds);
第二种写法不仅易于阅读,而且在执行时如果fail 也可以向测试人员提供更多的信息。
Junit 也有支持浮点数的assert方法,干净利索如下例
assertEquals ("some message", result, expected, delta);
另外要一提的是:
assertSame()用来测试两个引用是否指向同一个对象
assertEquals()用来测试两个对象是否相等
经验十一、确保测试代码与时间无关
尽量避免可能过期的测试数据,这种数据应该可以手工或者自动的刷新。另外还有一个技巧就是在使用这些
数据前更改系统的当前日期,数据操作结束后再恢复日期。当然,使用这一技巧要注意可能的副作用。
经验十二、使用文档生成器做测试文档
我们当然可以使用文本编辑器来书写单元测试的文档,但是更好的方法是使用文档生成器比如JavaDoc自
动生成,这样我们就不需担心实现和文档之间的同步问题。自动生成的文档格式统一错误也少。
分享到:
相关推荐
在Java开发领域,JUnit...持续进行单元测试是保证代码质量的重要手段,随着经验的积累,你将更加熟练地运用JUnit,编写出更高效的测试代码。记得经常重构和更新测试用例,以适应代码的变化,保持测试的覆盖性和有效性。
JUnit是Java编程语言中最常用的单元测试框架之一,它允许开发者编写可执行的测试用例来验证代码的功能。...无论是新手还是经验丰富的开发者,深入掌握JUnit都是提高开发效率和代码质量的必要步骤。
### Junit5用户手册知识点概览 #### 一、概述 **1.1 什么是JUnit5?** JUnit5,作为新一代的单元测试框架,与之前的...无论是对于新手还是经验丰富的开发人员,JUnit5都是一个值得深入学习和使用的强大测试框架。
《JUnit in Action》和《JUnit Recipes》是两本关于Java单元测试的重要书籍,它们深入浅出地介绍了如何使用JUnit框架进行高效、可靠的测试。JUnit是一个流行的开源测试框架,广泛用于Java应用程序的单元测试,它提供...
JUnit5是Java编程语言中最流行的单元测试框架之一,它的最新版本带来了许多改进和新特性,使得测试更加高效和灵活。...对于有经验的开发者,`junit5.jar`和`junit5-r5.4.0.zip`将有助于理解和定制JUnit5的运行行为。
本文将详细介绍如何在Android项目中设置和使用JUnitTest用例,以及如何通过JUnitTestDemo来理解其工作原理。 1. **JUnit介绍** JUnit是一个开源的、用于Java编程语言的单元测试框架。它为编写可重复运行的测试用例...
- **模拟对象与依赖注入**:介绍如何使用Mockito等工具与JUnit 5结合,实现模拟对象的测试。 总之,JUnit 5的r5.5.2版本在保持易用性的同时,带来了更多的灵活性和可扩展性。无论是新手还是经验丰富的开发者,都...
Junit5-Samples是一个开源项目,它为开发者提供了一个丰富的示例集合,旨在...这个开源项目对于初学者和有经验的测试人员都是宝贵的资源,不仅可以学习到最佳实践,还能了解到如何利用JUnit 5提高测试的质量和效率。
JUnit是Java编程语言中最常用的单元测试框架之一,它允许开发者编写可重复运行的测试用例,以确保代码的正确性和稳定性。...了解和掌握JUnit的安装和使用,对于任何Java开发者来说都是至关重要的。
本书详尽地探讨了如何有效地使用JUnit进行软件测试,包括基础概念、高级特性和最佳实践,旨在帮助读者提升软件质量,降低维护成本,以及通过自动化测试提升开发效率。 在书中,作者首先介绍了JUnit的基本概念,包括...
总的来说,这个压缩包提供了一个便捷的方式获取和使用JUnit,无论你是新手还是经验丰富的开发者,都可以从中找到你需要的资源。通过阅读文档,理解和实践JUnit,你可以确保你的Java项目具有高质量的单元测试,从而...
《单元测试之道Java版:使用JUnit》是一本深入讲解如何在Java开发中运用JUnit进行高效自动化测试的专业书籍。JUnit作为Java领域最广泛使用的单元测试框架,对于任何希望提升软件质量、确保代码健壮性的开发者来说,...
### Junit简介及扩展 #### Junit简介 JUnit是一个广泛使用的Java单元测试框架,由Kent Beck和Erich Gamma创建。...无论是对于初学者还是经验丰富的开发者来说,掌握JUnit的基本用法和高级特性都是非常有价值的。
### JUnit5学习入门知识点详解 #### 一、JUnit简介 JUnit是一个开源的Java单元测试框架,被广泛应用于软件开发中的...无论是初学者还是经验丰富的开发者,都应该掌握JUnit的基本用法,以便更好地进行软件开发工作。
JUnit学习和经验分享 JUnit是一个广泛使用的Java测试框架,旨在简化测试的编写、提高测试的效率和简化测试的维护。下面我们将详细介绍JUnit的学习和经验分享。 JUnit的设计目标 JUnit的设计目标是简化测试的编写...
JUnit 4.10 是一个广泛使用的开源Java测试框架,由Ernst Kormann和Kent Beck开发,基于JUnit 3.x版本进行了重大改进。它在软件开发过程中扮演着至关重要的角色,尤其是对于遵循敏捷开发原则的团队。JUnit 4.10是...
JUnit是Java编程语言中最常用的单元测试框架之一,它为开发者提供了编写和运行可重复的、自动化的测试用例的能力。...无论是初学者还是经验丰富的开发者,理解并运用JUnit都是提高开发效率的关键步骤。
《单元测试之道Java版-使用JUnit》是一本深入讲解如何在Java开发中运用JUnit进行单元测试的专业指南。单元测试是软件开发过程中的重要环节,它能够确保代码的正确性、可维护性和稳定性。JUnit作为Java领域最流行的...
7. **假对象(Mock Objects)与模拟(Mocks)**:虽然JUnit本身不包含mocking功能,但通常会配合使用如Mockito这样的库进行模拟对象的创建,以隔离测试环境。 8. **测试驱动开发(TDD)**:JUnit是TDD实践中不可或...