本文举例源代码或素材下载
JUnit 测试框架被越来越多
![](/icons/47478de.gif)
开发小组所共同使用
![](/icons/47478dou2.gif)
归功于各种各样
![](/icons/47478de.gif)
测试装具模块
![](/icons/47478dou.gif)
现在可以测试构成任何 Java 应用
![](/icons/47478chengxu.gif)
![](/icons/47478de.gif)
几乎每
![](/icons/47478yi.gif)
个组件
![](/icons/47478dou2.gif)
事实上
![](/icons/47478dou.gif)
几乎整个 2级市场似乎都是用围绕 Junit 建立
![](/icons/47478de.gif)
![](/icons/47478dou2.gif)
包括 Cactus、jfcUnit、XMLUnit、DbUnit 和 HttpUnit 这样
![](/icons/47478de.gif)
装具模块都可以免费供开发人员用于测试应用
![](/icons/47478chengxu.gif)
![](/icons/47478dou2.gif)
随着系统
![](/icons/47478de.gif)
复杂程度
![](/icons/47478de.gif)
增加
![](/icons/47478dou.gif)
并且有这么多工具可供使用
![](/icons/47478dou.gif)
没有什么理由不依靠单元测试
![](/icons/47478dou2.gif)
不过
![](/icons/47478dou.gif)
开发人员不仅仅是
![](/icons/47478chengxu.gif)
员
![](/icons/47478dou2.gif)
我们和用户交互以修复 bug 并确定需求
![](/icons/47478dou2.gif)
我们参加会议并进行电话推销
![](/icons/47478dou2.gif)
我们完成
![](/icons/47478yi.gif)
些(有时全部)质量保证功能
![](/icons/47478dou2.gif)
既然有这么多责任
![](/icons/47478dou.gif)
希望尽可能自动化就是自然而然
![](/icons/47478de.gif)
了
![](/icons/47478dou2.gif)
![](/icons/47478yinwei.gif)
好
![](/icons/47478de.gif)
团队(Team)(除了其他事情外)会进行大量测试
![](/icons/47478dou.gif)
希望自动化区别
![](/icons/47478de.gif)
开发过程
![](/icons/47478de.gif)
人常常会对这
![](/icons/47478yi.gif)
领域进行详细研究
![](/icons/47478dou2.gif)
自动化单元测试
有许多种自动化所有项目测试用例
![](/icons/47478de.gif)
定位和执行
![](/icons/47478de.gif)
思路方法
![](/icons/47478dou2.gif)
![](/icons/47478yi.gif)
种解决方案是联合使用 Ant
![](/icons/47478de.gif)
junit 任务和嵌入
![](/icons/47478de.gif)
file
![](/icons/47478set.gif)
任务
![](/icons/47478dou2.gif)
这样就可以包括和排除特定目录中
![](/icons/47478de.gif)
文件(基于文件名样式)
![](/icons/47478dou2.gif)
另
![](/icons/47478yi.gif)
种选择是使用 Eclipse
![](/icons/47478de.gif)
![](/icons/47478yi.gif)
个功能
![](/icons/47478dou.gif)
它可以指定所有测试所在
![](/icons/47478de.gif)
和执行
![](/icons/47478de.gif)
目录
![](/icons/47478dou2.gif)
前
![](/icons/47478yi.gif)
种选择提供了对运行
![](/icons/47478de.gif)
测试进行过滤
![](/icons/47478de.gif)
灵活性(并且由于它是
![](/icons/47478yi.gif)
个纯粹
![](/icons/47478de.gif)
无头(headless)Java 应用
![](/icons/47478chengxu.gif)
![](/icons/47478dou.gif)
可以运行在几乎所有地方)
![](/icons/47478dou.gif)
后
![](/icons/47478yi.gif)
种选择可以调试“动态”包
![](/icons/47478dou2.gif)
是否可以结合这两种方式
![](/icons/47478de.gif)
强大和灵活性?
有了 Python 编程语言
![](/icons/47478de.gif)
Java 平台实现 -- Jython
![](/icons/47478dou.gif)
回答是响亮
![](/icons/47478de.gif)
“可以!”(如果不熟悉 Jython
![](/icons/47478dou.gif)
应当在继续本文的前补充这方面知识
![](/icons/47478dou.gif)
更多信息请参阅后面
![](/icons/47478de.gif)
参考资料)
![](/icons/47478dou2.gif)
利用 Jython
![](/icons/47478de.gif)
强大和优雅
![](/icons/47478dou.gif)
可以维护
![](/icons/47478yi.gif)
个定位文件系统、搜索匹配某种样式
![](/icons/47478de.gif)
类和动态编译 JUnit TestSuite 类
![](/icons/47478de.gif)
脚本
![](/icons/47478dou2.gif)
这个 TestSuite 类像所有其他静态定义
![](/icons/47478de.gif)
类
![](/icons/47478yi.gif)
样
![](/icons/47478dou.gif)
可以用喜爱
![](/icons/47478de.gif)
调试
![](/icons/47478chengxu.gif)
容易地调试
![](/icons/47478dou2.gif)
(在本文中使用
![](/icons/47478de.gif)
例子假定使用
![](/icons/47478de.gif)
是 Eclipse IDE
![](/icons/47478dou.gif)
不过
![](/icons/47478dou.gif)
我在这里描述
![](/icons/47478de.gif)
技术不用做很多修改就可以用于大多数其他 IDE
![](/icons/47478dou2.gif)
)
在进行任何设计决定时
![](/icons/47478dou.gif)
必须对所做
![](/icons/47478de.gif)
选择和决定
![](/icons/47478de.gif)
影响进行权衡
![](/icons/47478dou2.gif)
在这里
![](/icons/47478dou.gif)
为了得到调试动态生成
![](/icons/47478de.gif)
测试包
![](/icons/47478de.gif)
能力
![](/icons/47478dou.gif)
必须增加额外
![](/icons/47478de.gif)
复杂性
![](/icons/47478dou2.gif)
不过
![](/icons/47478dou.gif)
这种复杂性被 Jython 自身所减轻了:Jython 经过很好测试并得到很好
![](/icons/47478de.gif)
支持
![](/icons/47478dou.gif)
并且是开放源代码
![](/icons/47478de.gif)
![](/icons/47478dou2.gif)
而且,Python 越来越成为面向对象
![](/icons/47478de.gif)
、平台独立
![](/icons/47478de.gif)
编程
![](/icons/47478de.gif)
事实上
![](/icons/47478de.gif)
标准
![](/icons/47478dou2.gif)
出于这两种原因
![](/icons/47478dou.gif)
采用 Jython
![](/icons/47478de.gif)
风险很少
![](/icons/47478dou.gif)
特别是它提供了这样
![](/icons/47478de.gif)
好处:在创建和调试动态生成
![](/icons/47478de.gif)
JUnit TestSuite 类方面具有无可匹敌
![](/icons/47478de.gif)
灵活性
如果是否采用 Jython 是主要
![](/icons/47478de.gif)
考虑
![](/icons/47478dou.gif)
那么即使不使用它也可以在解决原来
![](/icons/47478de.gif)
问题方面有所进展
![](/icons/47478dou2.gif)
不使用 Jython
![](/icons/47478de.gif)
话
![](/icons/47478dou.gif)
可以用
![](/icons/47478yi.gif)
个 Java Property 文件存储
![](/icons/47478yi.gif)
组类、目录和包
![](/icons/47478dou.gif)
以在包中加入或者排除测试
![](/icons/47478dou2.gif)
不过
![](/icons/47478dou.gif)
如果选择使用 Jython
![](/icons/47478dou.gif)
就可以利用整个 Python 语言和运行时来解决选择执行哪些测试
![](/icons/47478de.gif)
问题
![](/icons/47478dou2.gif)
Python 脚本比 Java Property 文件灵活得多
![](/icons/47478dou.gif)
它只受限于您
![](/icons/47478de.gif)
想像力
![](/icons/47478dou2.gif)
利用 Jython 和 Java 平台
![](/icons/47478de.gif)
无缝集成可以创建静态定义
![](/icons/47478de.gif)
、然而是动态构建
![](/icons/47478de.gif)
TestSuite 类
![](/icons/47478dou2.gif)
有大量有关 JUnit
![](/icons/47478de.gif)
教程
![](/icons/47478dou.gif)
不过还是看下面这两行代码作为复习
![](/icons/47478dou2.gif)
清单 1 是静态构建 TestSuite 类
![](/icons/47478de.gif)
![](/icons/47478yi.gif)
个例子(这个例子取自 JUnit: A Cook's Tour
![](/icons/47478dou.gif)
有关它和其他 JUnit 资源
![](/icons/47478de.gif)
链接请参阅 参考资料):
清单 1.静态定义 TestSuite
public
Test suite
{
TestSuite( MoneyTest.
);
}
清单 1 表明 TestSuite 是由 Test 类
![](/icons/47478de.gif)
类例子组成
![](/icons/47478de.gif)
![](/icons/47478dou2.gif)
这个装具模块完全利用了这
![](/icons/47478yi.gif)
点
![](/icons/47478dou2.gif)
为了分析这个工具
![](/icons/47478de.gif)
代码
![](/icons/47478dou.gif)
应从 参考资料中下载本文
![](/icons/47478de.gif)
举例 JAR 文件
![](/icons/47478dou2.gif)
这个文档包含两个文件:DynamicTestSuite.java 和 getalltests.py
![](/icons/47478dou.gif)
前者是
![](/icons/47478yi.gif)
个用 Phthon 脚本动态生成 TestSuite
![](/icons/47478de.gif)
JUnit 测试装具模块
![](/icons/47478dou.gif)
后者是
![](/icons/47478yi.gif)
个搜索匹配特定样式
![](/icons/47478de.gif)
文件
![](/icons/47478de.gif)
Python 脚本
![](/icons/47478dou2.gif)
DynamicTestSuite.java 使用 getalltests.py 构建 TestSuite
![](/icons/47478dou2.gif)
可以修改 getalltests.py 以更好地适合自己
![](/icons/47478de.gif)
项目
![](/icons/47478de.gif)
需要
![](/icons/47478dou2.gif)
了解测试装具模块
代码是如何工作
![](/icons/47478de.gif)
?首先
![](/icons/47478dou.gif)
指派 getalltests.py 获取
![](/icons/47478yi.gif)
组要执行
![](/icons/47478de.gif)
Test 类
![](/icons/47478dou2.gif)
然后
![](/icons/47478dou.gif)
使用 Jython API 将这个列表从 Python 运行时环境中提取出来
![](/icons/47478dou2.gif)
然后使用 Java Reflection API 构建在表示 Test 类名
![](/icons/47478de.gif)
列表中
![](/icons/47478de.gif)
String 对象
![](/icons/47478de.gif)
类例子
![](/icons/47478dou2.gif)
最后
![](/icons/47478dou.gif)
用 JUnit API 将 Test 添加到 TestSuite 中
![](/icons/47478dou2.gif)
这 4个库
![](/icons/47478de.gif)
相互配合可以实现您
![](/icons/47478de.gif)
目标:动态构建
![](/icons/47478de.gif)
TestSuite 可以像静态定义
![](/icons/47478de.gif)
那样运行
看
![](/icons/47478yi.gif)
下清单 2 中
![](/icons/47478de.gif)
JUnit suite 清单
![](/icons/47478dou2.gif)
它是
![](/icons/47478yi.gif)
个公开 public
![](/icons/47478static.gif)
TestSuite suite
![](/icons/47478kh.gif)
思路方法签名
![](/icons/47478de.gif)
TestCase
![](/icons/47478dou2.gif)
由 JUnit 框架
![](/icons/47478diaoyong.gif)
![](/icons/47478de.gif)
suite
![](/icons/47478kh.gif)
思路方法
![](/icons/47478diaoyong.gif)
getTestSuite
![](/icons/47478dou.gif)
getTestSuite
![](/icons/47478kh.gif)
又
![](/icons/47478diaoyong.gif)
getClassNamesViaJython
![](/icons/47478kh.gif)
以获取
![](/icons/47478yi.gif)
组 String 对象
![](/icons/47478dou.gif)
其中每
![](/icons/47478yi.gif)
个对象表示
![](/icons/47478yi.gif)
个作为包
![](/icons/47478de.gif)
![](/icons/47478yi.gif)
部分
![](/icons/47478de.gif)
TestCase 类
清单 2. 动态定义 TestSuite
/**
* @
TestSuite A test suite containing all our tests (as found by Python script)
*/
private TestSuite getTestSuite
{
TestSuite suite =
TestSuite
;
// get Iterator to
names we're going to add to our Suite
Iterator testClassNames = getClassNamesViaJython
.iterator
;
while( testClassNames.hasNext
) {
String
name = testClassNames.next
.toString
;
try {
// construct a Class object given the test
name
Class testClass = Class.forName(
name );
// add to our suite
suite.addTestSuite( testClass );
.out.pr
ln( "Added: " +
name );
}
catch( ClassNotFoundException e ) {
StringBuffer warning =
StringBuffer
;
warning.append( "Warning: Class '" ).append(
name ).append( "' not found." );
.out.pr
ln( warning.toString
);
}
}
suite;
}
在开始时
![](/icons/47478dou.gif)
要保证设置了正确
![](/icons/47478de.gif)
系统属性
![](/icons/47478dou2.gif)
在内部
![](/icons/47478dou.gif)
Jython 将使用 python.home 属性来定位它所需要
![](/icons/47478de.gif)
文件
![](/icons/47478dou2.gif)
最终会
![](/icons/47478diaoyong.gif)
getClassNamesViaJython
![](/icons/47478kh.gif)
思路方法
![](/icons/47478dou.gif)
在这里面会有
![](/icons/47478yi.gif)
些奇妙
![](/icons/47478de.gif)
事情发生
![](/icons/47478dou.gif)
如在清单 3 中将会看到
![](/icons/47478de.gif)
清单 3. 从 Python 运行时提取 Java 对象
/**
* Get list of tests we're going to add to our suite
* @
List A List of String objects, each representing
name of a TestCase
*/
private List getClassNamesViaJython
{
// run python script
erpreter.execfile( getPathToScript
);
// extract out Python object named PYTHON_OBJECT_NAME
PyObject allTestsAsPythonObject =
erpreter.get( PYTHON_OBJECT_NAME );
// convert the Python object to a String![](/icons/47478zhk2.gif)
String
allTests = (String
) allTestsAsPythonObject.__tojava__( String
.
);
// add all elements of
.gif' /> to a List
List testList =
ArrayList
;
testList.addAll( Arrays.asList( allTests ) );
testList;
}
首先
![](/icons/47478dou.gif)
对 Python 文件进行判断
![](/icons/47478dou2.gif)
然后
![](/icons/47478dou.gif)
从 Python 运行时提取出
![](/icons/47478yi.gif)
个 PyObject
![](/icons/47478dou2.gif)
这就是得到
![](/icons/47478de.gif)
对象
![](/icons/47478dou.gif)
它包含将构成测试包
![](/icons/47478de.gif)
所有测试用例
![](/icons/47478de.gif)
类名(记住 -- PyObject 是 Python 对象
![](/icons/47478de.gif)
Java 运行时对应物)
![](/icons/47478dou2.gif)
然后创建具体
![](/icons/47478de.gif)
List 并用 PyObject 填充它
![](/icons/47478dou.gif)
使用 __tojava__ 指示 PyObject 将其内容转换为
![](/icons/47478yi.gif)
个 Java String
![](/icons/47478shuzu.gif)
![](/icons/47478dou2.gif)
最后
![](/icons/47478dou.gif)
将控制返回 getTestSuite
![](/icons/47478dou.gif)
在这里装载 Jython 标识
![](/icons/47478de.gif)
测试用例
![](/icons/47478dou.gif)
并将它们添加到组合包(composite)中
![](/icons/47478dou2.gif)
在开发环境中安装测试装具模块
现在对于测试装具模块如何工作已经有了很好
![](/icons/47478de.gif)
认识
![](/icons/47478dou.gif)
可能迫不及待要自己试试它了
![](/icons/47478dou2.gif)
您将需要完成以下步骤以配置 Eclipse 来运行这个装具模块
![](/icons/47478dou2.gif)
(如果使用区别
![](/icons/47478de.gif)
IDE
![](/icons/47478dou.gif)
应当可以容易地针对您
![](/icons/47478de.gif)
环境修改这些步骤
![](/icons/47478dou2.gif)
)
安装 Jython 2.1
![](/icons/47478dou.gif)
如果还没安装
![](/icons/47478de.gif)
话
![](/icons/47478dou2.gif)
(链接请见 参考资料)
![](/icons/47478dou2.gif)
拷贝 getalltests.py 到主目录
![](/icons/47478dou2.gif)
编辑 getalltests.py 第 25 行以指定到源文件
![](/icons/47478de.gif)
根路径
![](/icons/47478dou.gif)
会搜索在这个位置下
![](/icons/47478de.gif)
所有目录中和 org 包中 *Text.java 匹配
![](/icons/47478de.gif)
文件名
![](/icons/47478dou2.gif)
如果有必要
![](/icons/47478dou.gif)
修改第 54 行以改变根包名(例如
![](/icons/47478dou.gif)
改为 com)
![](/icons/47478dou2.gif)
将 DynamicTestSuite.java 拷贝到源树中
![](/icons/47478dou2.gif)
将以下 JAR 添加到 Eclipse 项目中: junit.jar (JUnit 框架 2进制文件
![](/icons/47478dou.gif)
下载信息请参阅 JUnit
![](/icons/47478de.gif)
Web 网站WebSite)
jython.jar(Jython 2进制文件
![](/icons/47478dou.gif)
位于 Jython 安装目录)
![](/icons/47478dou2.gif)
将 DynamicTestSuite 类装载到 Eclipse Java 源文件编辑器中
![](/icons/47478dou2.gif)
执行以下步骤的
![](/icons/47478yi.gif)
: 在 Package Explorer 视图中选择 DynamicTestSuite
![](/icons/47478dou.gif)
或者
按 Ctrl+Sh
![](/icons/47478if.gif)
t+T并在 Choose Type 输入字段键入 DynamicTestSuite
![](/icons/47478dou2.gif)
从文件菜单栏选择 Run
![](/icons/47478dou.gif)
然后选择 Debug...
![](/icons/47478dou2.gif)
选择 JUnit配置
![](/icons/47478dou2.gif)
单击 New按钮
![](/icons/47478dou2.gif)
将会创建
![](/icons/47478yi.gif)
个新
![](/icons/47478de.gif)
JUnit 目标
![](/icons/47478dou.gif)
DynamicTestSuite 应当预填入 Test Class 字段
![](/icons/47478dou2.gif)
选择 Arguments选项卡
![](/icons/47478dou2.gif)
在 VM 参数文本框中键入 -Dpython.home=<path where you
![](/icons/47478install.gif)
ed Jython>
![](/icons/47478dou2.gif)
单击 Debug按钮
变!现在就有了
![](/icons/47478yi.gif)
个具体
![](/icons/47478de.gif)
JUnit TestCase 类
![](/icons/47478dou.gif)
可以像静态定义
![](/icons/47478de.gif)
包那样处理它
![](/icons/47478dou2.gif)
设置边界并进行调试!不需要修改 Test 类
![](/icons/47478dou.gif)
装具模块将构建
![](/icons/47478yi.gif)
个包
![](/icons/47478dou.gif)
就像您显式将每
![](/icons/47478yi.gif)
个 Class 对象编写到包中
![](/icons/47478yi.gif)
样
![](/icons/47478dou2.gif)
如要执行测试
![](/icons/47478dou.gif)
可以通过喜爱
![](/icons/47478de.gif)
调试器、编译工具(如 Ant 或 CruiseControl)
![](/icons/47478dou.gif)
或者
![](/icons/47478yi.gif)
个 JUnit 内含
![](/icons/47478de.gif)
test runner
![](/icons/47478diaoyong.gif)
这个装具模块
扩展这个装具模块
我相信您注意到了除非在运行前修改源代码
![](/icons/47478dou.gif)
否则这个装具模块只能用于
![](/icons/47478yi.gif)
个项目
![](/icons/47478dou2.gif)
可以容易地扩展这个装具模块让它支持多个项目
![](/icons/47478dou2.gif)
![](/icons/47478yi.gif)
种简单
![](/icons/47478de.gif)
方式是修改 getPathToScript
![](/icons/47478kh.gif)
以使用指定特定于项目
![](/icons/47478de.gif)
属性
![](/icons/47478de.gif)
系统属性
![](/icons/47478dou2.gif)
可以在自己
![](/icons/47478de.gif)
项目中自由使用它
![](/icons/47478dou.gif)
可以不加改变地使用它
![](/icons/47478dou.gif)
也可以以它为基础进行加工
![](/icons/47478dou2.gif)
不过
![](/icons/47478dou.gif)
请别忘记它
![](/icons/47478de.gif)
GPL 许可证