Junit单元测试

参考博文地址http://www.blogjava.net/jnbzwm/archive/2010/12/15/340801.html

Junit3

setup方法一般会初始化字段、准备数据。例如下面的 setUp() 方法,用于设定要加载的路由文件:

public void setUp() {
// 加载此测试用例的servicerouting配置文件
ServiceRouting.loadConfig("com/demo/servicerouting.conf");
// 
}


Junit4

我们仍然可以在每个测试方法运行之前初始化字段或准备数据。然而,完成这些操作的方法不再需要叫做 setUp(),只要用 @Before 注释来指示该方法即可,如下所示:

@Before
public void initialize() {
// 加载此测试用例的servicerouting配置文件
ServiceRouting.loadConfig("com/demo/servicerouting.conf");
// 
}


JUnit4允许我们使用 @Before 来注释多个方法,这些方法都在每个测试之前运行:

@Before
public void initialize() {
// 加载此测试用例的servicerouting配置文件
ServiceRouting.loadConfig("com/demo/servicerouting.conf");
// 
}
@Before
public void prepareRetData() {
//
}

@After

在 JUnit3 中,我们要将方法命名为 tearDown() 才可以实现清除方法,但在JUnit4中,只要给方法添加@After标注即可。

@After
public static void clearContext() {
ActionContext.getContext().put(StrutsStatics.HTTP_REQUEST, null);
ActionContext.setContext(null);
}

@BeforeClass和@AfterClass

任何用 @BeforeClass 注释的方法都将在该类中的测试方法运行之前刚好运行一次,而任何用 @AfterClass 注释的方法都将在该类中的所有测试都运行之后刚好运行一次。
例如,假设类中的每个测试都使用一个数据库连接、一个非常大的数据结构,或者申请其他一些资源。不要在每个测试之前都重新创建它,您可以创建它一次,用完后将其销毁清除。该方法将使得有些测试案例运行起来快得多。

 @BeforeClass

public static void classInit() {

Map callRet = new HashMap();

List<ErrorCodeMessageBean> list = new ArrayList<ErrorCodeMessageBean>();

list.add(createMsgBean("TDE0001", "第一个错误消息"));

list.add(createMsgBean("TDP9999", "格式化{0}{1}"));

list.add(createMsgBean("TDE1000~TDF0001", "区间错误消息"));

list.add(createMsgBean("TDG0001~", "有下限的区间错误消息"));

list.add(createMsgBean("~TDD0001", "有上限的区间错误消息"));

list.add(createMsgBean("~", "默认的消息"));

callRet.put(ErrorCodeMessageBean.codeMsgBeanKey, list);

ServiceCall.expectLastCallReturn(callRet);

}

@Test

public void oneTestMethod() {

//.

}

@AfterClass

public static void classDestroy() {

ServiceCall.expectLastCallReturn(null);

}



测试异常@Test(expected=XXXException.class)

旧式的异常测试是在抛出异常的代码中放入 try 块,然后在 try 块的末尾加入一个 fail() 语句。
例如,该方法测试被零除抛出一个 ArithmeticException:

public void testDivisionByZero() {
try {
int n = 2 / 0;
fail("Divided by zero!");
}
catch (ArithmeticException success) {
assertNotNull(success.getMessage());
}
}

该方法不仅难看,而且写起来也繁琐。在 JUnit 4 中,我们现在可以编写抛出异常的代码,并使用注释来声明该异常是预期的:

 @Test(expected = BusinessException.class)
public void testExecuteNameEmpty() throws Exception {
BookList bListAction = new BookList();
bListAction.setName("");
bListAction.execute();
}

附被测试代码(如果输入name为empty,则抛出BusinessException,若name不为"liming",则抛出MessageException异常):

 @Override

public String execute() throws Exception {

if (StringUtils.isEmpty(name)) {

throw new BusinessException("~", "name cant't empty.");

}

if (!StringUtils.equals("liming", name.trim())) {

throw new MessageException(name + " have no limits.");

}

Map ret = serviceCall.call(JMockService.queryDtlInfo, null);

orderId = (String) ret.get("OrderId");

dataList = (List) ret.get("Data");

return SUCCESS;

}


参数化测试

为了保证单元测试的严谨性,我们经常要模拟很多种输入参数,来确定我们的功能代码是可以正常工作的,为此我们编写大量的单元测试方法。可是这些测试方法都是大同小异:代码结构都是相同的,不同的仅仅是测试数据和期望输出值。
JUnit4 的参数化测试方法给我们提供了更好的方法,将测试方法中相同的代码结构提取出来,提高代码的重用度,减少复制粘贴代码的痛苦。
例如下面的功能代码(格式化字符串,将驼峰规则的字符串以"_"分隔):


public class WordDealUtil {
public static String wordFormat4DB(String name) {
if (name == null) {
return null;
}
Pattern p = Pattern.compile("[A-Z]");
Matcher m = p.matcher(name);
StringBuffer sb = new StringBuffer();
while (m.find()) {
if (m.start() != 0) {
m.appendReplacement(sb, ("_" + m.group()).toLowerCase());
}
}
return m.appendTail(sb).toString().toLowerCase();
}
}


没有使用参数化的测试用例代码:

public class WordDealUtilTest {

/**

* 测试 null 时的处理情况

*/

@Test

public void wordFormat4DBNull() {

String target = null;

String result = WordDealUtil.wordFormat4DB(target);

assertNull(result);

}

/**

* 测试空字符串的处理情况

*/

@Test

public void wordFormat4DBEmpty() {

String target = "";

String result = WordDealUtil.wordFormat4DB(target);

assertEquals("", result);

}

/**

* 测试当首字母大写时的情况

*/

@Test

public void wordFormat4DBegin() {

String target = "EmployeeInfo";

String result = WordDealUtil.wordFormat4DB(target);

assertEquals("employee_info", result);

}

/**

* 测试当尾字母为大写时的情况

*/

@Test

public void wordFormat4DBEnd() {

String target = "employeeInfoA";

String result = WordDealUtil.wordFormat4DB(target);

assertEquals("employee_info_a", result);

}

/**

* 测试多个相连字母大写时的情况

*/

@Test

public void wordFormat4DBTogether() {

String target = "employeeAInfo";

String result = WordDealUtil.wordFormat4DB(target);

assertEquals("employee_a_info", result);

}

}


看以上测试用例代码,结构相似,只是输入值与期望输出不同而已,但我们要拷贝很多代码。
使用参数化的测试用例代码:

@SuppressWarnings("unchecked")

@RunWith(Parameterized.class)

public class WordDealUtilTestWithParam {

private String expected;

private String target;

@Parameters

public static Collection words() {

return Arrays.asList(new Object[][] {

{ "employee_info", "employeeInfo" },  // 测试一般的处理情况

{ null, null },                         // 测试 null 时的处理情况

{ "", "" },                             // 测试空字符串时的处理情况

{ "employee_info", "EmployeeInfo" },    // 测试当首字母大写时的情况

{ "employee_info_a", "employeeInfoA" }, // 测试当尾字母为大写时的情况

{ "employee_a_info", "employeeAInfo" }  // 测试多个相连字母大写时的情况

});

}

/**

* 参数化测试必须的构造函数

* @param expected     期望的测试结果,对应参数集中的第一个参数

* @param target     测试数据,对应参数集中的第二个参数

*/

public WordDealUtilTestWithParam(String expected, String target) {

this.expected = expected;

this.target = target;

}

/**

* 测试将 Java 对象名称到数据库名称的转换

*/

@Test

public void wordFormat4DB() {

Assert.assertEquals(expected, WordDealUtil.wordFormat4DB(target));

}

}




很明显,代码简单且很清晰了。在静态方法 words 中,我们使用二维数组来构建测试所需要的参数列表,其中每个数组中的元素的放置顺序并没有什么要求,只要和构造函数中的顺序保持一致就可以了。现在如果再增加一种测试情况,只需要在静态方法 words 中添加相应的数组即可,不再需要复制粘贴出一个新的方法出来了。
这种参数化的测试用例写法,很适用于一些共用的功能方法。



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值