何提取一个测试层次结构[1] 软件测试
问题:
如果有多个共享一些通用对象的测试模块,这些对象在用于实现测试模块的几个TestCase类中式重复的。你想要复用这些对象,而不是复制它们。
情景:
这个问题在编写客户端或端到端测试的时候尤为常见——就是那些为整个系统而不是为某个类所做的测试。偶尔Junit用户——那些几乎完全依赖 JUnit作为测试驱动的开发工具的人——会忘记那些从来不使用Junit的、非测试驱动程序员,因为JUnit提供了一个易于使用的Java框架来编写测试程序。说这些是因为一个测试驱动的程序员会说:“如果你有一个这么大的测试模块,那你的测试代码就太大了,必须要修改测试代码使其只专注于系统的一个方面。如果你这样做了,那问题就会得到解决”。其实说的没错,可是这话是典型的数学家的语言:专注,精确,然而毫无用处。
如果能够精简测试模块或者能够找出其中没有被所有测试所共享的部分,那就更好了:我们更喜欢简单的测试用例层次结构。如果已经无法将事情简化——或者是看不出该如何做--那也至少应该试着将通用的测试模块移到一个地方。
技巧:
解决这个问题的技巧说来很简单:尽管编写的是测试程序,但它们还是用方法和对象实现的,所以将其当作方法和对象来看待。一个从一组类中抽取重复行为的办法是,应用抽取超类的重构方法创建一个类层次结构。具体步骤如下:
1. 选择两个有通用测试模块的测试用例类。
2. 创建一个新的TestCase的子类,它是有重复的测试模块的测试用例类的超类。在这里,我们将这个新类叫做BaseFixture,这个是各人自己取的。
3. 将BaseFixture类声明为抽象类,没有必要创建这个类的实例。
4. 修改测试用例类代码,使其继承自BaseFixture而不是直接继承TestCase。
5. 将重复的测试模块复制到BaseFixture中——就是说,复制参数域和setUp()方法中初始化参数域的代码。可能需要修改参数域,将其声明成受保护的而不是私有的,否则测试用例类就不能使用它们。可以将参数域封装在一个受保护的get方法里面,但是在这个例子中我们觉得没必要这样做。
6. 从每一个测试用例类中删除测试模块参数域,从setUp()方法中删除已经移到BaseFixture中的代码。
7. 在每个测试用例类的setUp()方法的开始加上一行super.setUp()。
8. 现在可以重新编译运行此测试程序,来验证其行为并没有改变。可以对尽可能多的共享通过测试模块对象的测试用例类重复上述步骤。
另外在验证了做完上述步骤没有带来任何问题以后,要看看那些setUp()方法中是否可以删除一些空的或只有一行super.setUp()的方法。