在JUnit测试中,通常作为POJO的Java Bean都是一组简单的getter/setter方法,需要测试的不是这些Bean本身,而是对Bean的属性设置后,测试业务方法是否正常工作,例如,一个注册用户的类方法void register(Account account),需要对传入的Account Bean做初始化设置,然后,根据业务规则决定register方法是否应该执行成功或者抛出IllegalArgumentException。
倘若按照常规的Unit测试,需要考虑用户输入的许多种组合,在testXxx()方法中编写模拟用户输入的代码是冗长而繁琐的,不如直接通过Swing窗口手动设置Bean的属性,然后再执行业务方法,这样,虽然引入了输入界面,需要人工干预测试过程,却大大简化了编写测试用例的麻烦。
在TCK测试中,对许多UI组件的测试正是采用这种半自动的方法,因为只有测试人员本身才能看到UI测试的结果正确与否,计算机很难判断一个类似fillRect()的方法到底有没有在屏幕上正确绘制出来。
基于这种思想,为了测试许多种不同输入的组合,我们决定编写一个能根据Bean的属性自动生成输入窗口的小工具,以便在Unit测试过程中,能够由测试人员手动设定Bean的属性和期望的结果,然后,继续测试。
为了实现这个目的,我们设计了一个小工具,它能够实现:
1.根据传入的Bean自动为每个public setXxx()方法生成输入框;
2.由测试人员手动设置Bean的属性,然后,选择“Success”,“Failure”以决定这次测试的数据应该产生正确或错误的结果。
3.可以连续多次循环测试,直到测试人员点击“End”,结束本次测试。
整个工具被封装在一个BeanInputDialog类中,它继承自JFrame,只暴露了一个public static方法。下面,我们以一个Account Bean为例,测试用户输入的属性是否合法。
Account定义了4个字段和一个validate()方法来验证输入,如果不符合输入,validate()方法会抛出IllegalArgumentException。我们编写一个简单的TestCase:
package com.crackj2ee.test.util;
import junit.framework.TestCase;
public class AccountTest extends TestCase {
public void testAccount() {
int expect;
for(;;) {
Account account = new Account(); // 待测试的Bean
expect = BeanInputDialog.inputBean(account); // 用户在此输入
// 注意:直到用户输入完成,inputBean()方法才会返回,它是一个同步方法
// inputBean()方法返回一个int,表示本次测试的期望值
if(expect==BeanInputDialog.EXPECT_END) // 期望测试结束,退出循环
break;
if(expect==BeanInputDialog.EXPECT_SUCCESS) { // 期望测试成功
account.validate();
}
if(expect==BeanInputDialog.EXPECT_FAILURE) { // 期望测试失败
try {
account.validate();
fail("Not catch IllegalArgumentException!"); // 没有捕获到预期的异常
}
catch(IllegalArgumentException e) {
// OK!捕获到预期的异常
}
}
}
}
}
在Unit测试过程中,可以不断设置Bean的属性,以满足多种组合的测试用例。下面分别输入合法和非法的用户名,然后点击期望值测试:
需要注意的是,由于属性的获取是通过反射得到的,考虑到通常的设计原则,我们只读取public的setXxx()方法。
如果在测试人员输入前,Bean已经有了一些初始值,这个工具也能通过反射得到相应的getXxx()方法的值。要注意的是,对boolean和Boolean类型的属性,是根据getXxx()取得而不是根据isXxx()取得的。
目前,可以处理的属性类型包括java.lang.String,java.util.Date,short,int,long,float,double,boolean及其包装类型Short,Integer,Long,Float,Double和Boolean。还可以对其扩展,以便支持更多的类型。