Java中的异常测试框架JUnit使用上手指南
JUnit是由ErichGamma和KentBeck编写的一个回归测试框架(regressiontestingframework)。Junit测试是程序员测试,即白盒测试。该项目主页:http://www.junit.org/
使用JUnit时,主要都是通过继承TestCase类别来撰写测试用例,使用testXXX()名称来撰写单元测试。
用JUnit写测试真正所需要的就三件事:
1. 一个import语句引入所有junit.framework.*下的类。
2. 一个extends语句让你的类从TestCase继承。
3. 一个调用super(string)的构造函数。
功能类MathTool
packagecom.zj.c01; publicclassMathTool{ publicstaticintgcd(intnum1,intnum2){ intr=0; while(num2!=0){ r=num1%num2; num1=num2; num2=r; } returnnum1; } }
测试类MathToolTest
packagecom.zj.c01; importjunit.framework.TestCase; publicclassMathToolTestextendsTestCase{ publicMathToolTest(Stringname){ super(name); } publicvoidtestGcd(){ assertEquals(5,MathTool.gcd(10,5)); } }
我们在用JUnit测试方法异常的时候,最容易想到的办法就是用try…catch去捕获异常,需要断言以下几个条件:
1.确实抛出的异常
2.抛出异常的Class类型
3.抛出异常的具体类型,一般检查异常的message属性中包含的字符串的断定
所以常用的代码你可能会这么写:
@Test publicvoidtestBizException() { try{ Password.validate("123"); fail("Noexceptionthrown."); }catch(Exceptionex){ assertTrue(exinstanceofBizException); assertTrue(ex.getMessage().contains("error")); } }
这里被测试的方法是Password.validate()方法是否抛出了相应的异常,注意这里别漏try中的
fail(“NoExceptionthrown.”)
代码行,不然如果被测试的方法如果没有抛出异常的话,这个用例是通过的,而你预期的是要抛出异常的。
在JUnit4中,大可不必如此这般的去测试方法异常。虽然这样也能测定出是否执行出预期的异常来,但它仍有弊端,接下来会一对比就知道了,try…catch的方法,JUnit无法为你提示出详细的断言失败原因。
那么来看看自从JUnit4后可以怎么去测试异常呢?用@Test(execpted=Exception.class)注解就行,参考如下代码:
@Test(expected=BizException.class) publicvoidtestBizException() { Password.validate(null); }
如果被测试的方法有抛出BizException类型便是断言成功,对了@Test(expected=BizException.class)只能判断出异常的类型,并无相应的注解能断言出异常的更具体的信息,即无法判定抛出异常的message属性。
那么,有时候我们会在一个方法中多次抛出一种类型的异常,但原因不同,即异常的message信息不同,比如出现BizException时会有以下两种异常:
newBizException(“Passwordmustcontainsatleast6letters.”) newBizException(“Passwordlengthlessthan15letters”)
这就要有办法去断言异常的message了,针对于此,自JUnit4.7之后又给了我们更完美的选择,就是下面的代码:
@Rule publicExpectedExceptionexpectedEx=ExpectedException.none(); @Test publicvoidtestBizException()throwsInvalidPasswordException { expectedEx.expect(BizException.class); expectedEx.expectMessage("required"); Password.validate(""); }
上面代码需重点关注几个:
1.@Rule注解的ExpectedException变量声明,它必须为public
2.@Test处,不能写成@Test(expected=BizException.class),否则不能正确测试,也就是
@Test(expected=BizException.class)和测试方法中的expectedEx.expectXxx()方法是不能同时并存的
3.expectedEx.expectMessage()中的参数是Matcher或subString,就是说可用正则表达式判定,或判断是否包含某个子字符串
4.再就是有一点很重,把被测试方法写在expectedEx.expectXxx()方法后面,不然也不能正确测试的异常
5.最后一个是,只要测试方法直接抛出被测试方法的异常即可,并不影响你所关心的异常
前面说到用try…catch的办法也能正确测试到异常,@Test(expected=…)或@Rule与try…catch的方法对比有什么好处呢,显然用JUnit4推荐的方法简洁明了。再来看测试失败时JUnit会为你提示什么呢?
try…catch测试异常失败时,得到的提示:
无异常时:
java.lang.AssertionError:Noexceptionthrown. atorg.junit.Assert.fail(Assert.java:91) atcc.unmi.PasswordTest.passwordLengthLessThan6LettersThrowsException(PasswordTest.java:20)
异常类型不对或异常的message不对时:
java.lang.AssertionError: atorg.junit.Assert.fail(Assert.java:91) atorg.junit.Assert.assertTrue(Assert.java:43) atorg.junit.Assert.assertTrue(Assert.java:54) atcc.unmi.PasswordTest.passwordLengthLessThan6LettersThrowsException(PasswordTest.java:22)
上面能提供给我们的定位错误的帮助不是特别大
再看@Test(expected=BizException.class)时测试失败时的提示:
java.lang.AssertionError:Expectedexception:cc.test.BizException atorg.junit.internal.runners.statements.ExpectException.evaluate(ExpectException.java:32) atorg.junit.rules.ExpectedException$ExpectedExceptionStatement.evaluate(ExpectedException.java:110)
用@RulesExpectedException方式来测试异常,失败时的提示:
java.lang.AssertionError: Expected:(exceptionwithmessageastringcontaining“YES.required”andaninstanceofjava.lang.NullPointerException) got: atorg.junit.Assert.assertThat(Assert.java:778) atorg.junit.Assert.assertThat(Assert.java:736) atorg.junit.rules.ExpectedException$ExpectedExceptionStatement.evaluate(ExpectedException.java:114)
特别是@RulesExpectedException方法时为何测试失败提示的清清楚楚。期望什么异常,异常message中含何字符串,实际上确得到什么类型的异常,异常中message是什么。有了这,你一看到就知道怎么去修补你的程序。