用Jython构建JUnit测试包 | |
开 发人员有多种理由决定自动化单元测试。许多人甚至进一步发挥它,自动化这些测试的定位和执行。但是如果想要测试装具模块(test harness)像静态定义的那样运行呢?请跟随开发员 Michael Nadel,看看如何利用 Python 模拟静态定义的 JUnit TestSuite 类。 JUnit 测试框架被越来越多的开发小组所共同使用。归功于各种各样的测试装具模块,现在可以测试构成任何 Java 应用程序的几乎每一个组件。事实上,几乎整个二级市场似乎都是用围绕 Junit 建立的。包括 Cactus、jfcUnit、XMLUnit、DbUnit 和 HttpUnit 这样的装具模块都可以免费供开发人员用于测试应用程序。随着系统的复杂程度的增加,并且有这么多工具可供使用,没有什么理由不依靠单元测试。 不过,开发人员不仅仅是程序员。我们与用户交互以修复 bug 并确定需求。我们参加会议并进行电话推销。我们完成一些(有时全部)质量保证功能。既然有这么多责任,希望尽可能自动化就是自然而然的了。因为好的团队 (除了其他事情外)会进行大量测试,希望自动化不同的开发过程的人常常会对这一领域进行详细研究。 自动化单元测试 有了 Python 编程语言的 Java 平台实现 —— Jython,回答是响亮的“可以!”(如果不熟悉 Jython,应当在继续本文之前补充这方面知识,更多信息请参阅后面的 参考资料 )。利用 Jython 的强大和优雅,可以维护一个定位文件系统、搜索匹配某种样式的类和动态编译 JUnit TestSuite 类的脚本。这个 TestSuite 类像所有其他静态定义的类一样,可以用喜爱的调试程序容易地调试。(在本文中使用的例子假定使用的是 Eclipse IDE,不过,我在这里描述的技术不用做很多修改就可以用于大多数其他 IDE。) 在进行任何设计决定时,必须对所做的选择和决定的影响进行权衡。在这里,为了得到调试动态生成的测试包的能力,必须增加额外的复杂性。不过,这种复杂 性被 Jython 自身所减轻了:Jython 经过很好测试并得到很好的支持,并且是开放源代码的。而且,Python 越来越成为面向对象的、平台独立的编程的事实上的标准。出于这两种原因,采用 Jython 的风险很少,特别是它提供了这样的好处:在创建和调试动态生成的 JUnit TestSuite 类方面具有无可匹敌的灵活性。 如果是否采用 Jython 是主要的考虑,那么即使不使用它也可以在解决原来的问题方面有所进展。不使用 Jython 的话,可以用一个 Java Property 文件存储一组类、目录和包,以在包中加入或者排除测试。不过,如果选择使用 Jython,就可以利用整个 Python 语言和运行时来解决选择执行哪些测试的问题。Python 脚本比 Java Property 文件灵活得多,它只受限于您的想像力。 利用 Jython 与 Java 平台的无缝集成可以创建静态定义的、然而是动态构建的 TestSuite 类。有大量关于 JUnit 的教程,不过还是看下面这两行代码作为复习。清单 1 是静态构建 TestSuite 类的一个例子(这个例子取自 JUnit: A Cook's Tour,有关它和其他 JUnit 资源的链接请参阅 参考资料): 清单 1.静态定义 TestSuite 清单 1 表明 TestSuite 是由 Test 类的类实例组成的。这个装具模块完全利用了这一点。为了分析这个工具的代码,应从 参考资料 中下载本文的示例 JAR 文件。这个文档包含两个文件:DynamicTestSuite.java 和 getalltests.py,前者是一个用 Phthon 脚本动态生成 TestSuite 的 JUnit 测试装具模块,后者是一个搜索匹配特定样式的文件的 Python 脚本。DynamicTestSuite.java 使用 getalltests.py 构建 TestSuite。可以修改 getalltests.py 以更好地适合自己的项目的需要。 了解测试装具模块 看一下清单 2 中的 JUnit suite 清单。它是一个公开 public static TestSuite suite() 方法签名的 TestCase。由 JUnit 框架调用的 suite() 方法调用 getTestSuite(), getTestSuite() 又调用 getClassNamesViaJython() 以获取一组 String 对象,其中每一个对象表示一个作为包的一部分的 TestCase 类。 清单 2. 动态定义 TestSuite /** // get Iterator to class names we're going to add to our Suite while( testClassNames.hasNext() ) { try { // add to our suite System.out.println( "Added: " + classname ); return suite; 在开始时,要保证设置了正确的系统属性。在内部,Jython 将使用 python.home 属性来定位它所需要的文件。最终会调用 getClassNamesViaJython() 方法,在这里面会有一些奇妙的事情发生,如在清单 3 中将会看到的。 清单 3. 从 Python 运行时提取 Java 对象 // extract out Python object named PYTHON_OBJECT_NAME // convert the Python object to a String[] // add all elements of array to a List return testList; 首先,对 Python 文件进行判断。然后,从 Python 运行时提取出一个 PyObject。这就是得到的对象,它包含将构成测试包的所有测试用例的类名(记住 —— PyObject 是 Python 对象的 Java 运行时对应物)。然后创建具体的 List 并用 PyObject 填充它,使用 __tojava__ 指示 PyObject 将其内容转换为一个 Java String 数组。最后,将控制返回 getTestSuite(),在这里装载 Jython 标识的测试用例,并将它们添加到组合包(composite)中。 在开发环境中安装测试装具模块 安装 Jython 2.1,如果还没安装的话。(链接请见 参考资料 )。 拷贝 getalltests.py 到主目录。 编辑 getalltests.py 第 25 行以指定到源文件的根路径,会搜索在这个位置下的所有目录中与 org 包中 *Text.java 匹配的文件名。 如果有必要,修改第 54 行以改变根包名(例如,改为 com)。将 DynamicTestSuite.java 拷贝到源树中。将以下 JAR 添加到 Eclipse 项目中: junit.jar (JUnit 框架二进制文件,下载信息请参阅 JUnit 的 Web 网站)。 jython.jar(Jython 二进制文件,位于 Jython 安装目录)。将 DynamicTestSuite 类装载到 Eclipse Java 源文件编辑器中。执行以下步骤之一: 在 Package Explorer 视图中选择 DynamicTestSuite,或者 按 Ctrl+Shift+T 并在 Choose Type 输入字段键入 DynamicTestSuite。 从文件菜单栏选择 Run,然后选择 Debug...。 选择 JUnit 配置。 单击 New 按钮。将会创建一个新的 JUnit 目标,DynamicTestSui |