junit 测试异常(转)
? 今天做单元测试,要求做下路径覆盖,于是乎要对异常进行测试了。这个第一次搞,
不知道怎么下手,当遇到异常,马上终止测试(测试没有通过),但是不在catch中写的话,有不符合要求,于是网上转了下。算是解决了。
?测试是否抛出正确的异常(Test throwing the right exception)博客分类:- ?软件测试Java代码??
- public?void?testConstructorDiesWithNull()?throws?Exception{??
- ??try{??
- ??????Fraction?oneOverZero?=?new?Fraction(1,0);??
- ??????fail("Created?fraction?1/0!?That's?undefined!");??
- ????}??
- ?catch(IllegalArgumentException?excepted){??
- ?????assertEquals("denominator",excepted.getMessage());??
- ??????}??
- }??
- public?void?testForException(){??
- ??assertThrows(MyException.class,?new?ExceptionClosure(){??
- ?????public?Object?execute(Object?input)?throws?Exception{??
- ??????????return?doSomethingThatShouldThrowMyException();??
- ?????????}??
- ????});??
- ??}??
- ??public?static?void?assertThrows(Class?expectedExceptionClass,?ExceptionalClosure?closure){??
- ????String?expectedExceptionClassName?=?expectedExceptionClass.getName();??
- ??
- ?try{??
- ???????closure.execute(null);??
- ???????fail("Block?did?not?throw?an?exception?of?type"+expectedExceptionClassName);??
- ?????}??
- catch(Exception?e){??
- ???assertTrue("Caught?exception?of?type?<"?+?e.getClass().getName()??
- ??????????????+">,?expected?one?of?type?<"??
- ??????????????+expectedExceptionClassName+">",??
- ??????????????expectedExceptionClass.isInstance(e));??
- }??
- }??
- public?void?testNameNoEntered(){??
- ?try{??
- ?????log("",?"password");??
- ?????fail("User?logged?in?without?a?user?name?!");??
- ?????}??
- atch(MissingEntryException?expected){??
- ????assertEquals("userName",?expected.getEnteryName());??
- ????assertEquals("Please?enter?a?user?name?and?try?again",??
- ??expected.getMessage());??
- ??
- ?????}??
- }??

首先分析上面的代码?
1. 找到可能抛出异常的代码段,将它放入一个try语句内。?
2. 调用了应该抛出异常的方法以后,在try语句内写一个fail()方法来说明:“如果运行到了这里,那么说明期望的异常没有被抛出”。?
3. 添加一个catch语句以捕获期望的异常。?
4. 在catch语句内,如果需要的话,验证捕获的异常的属性与你期望的相同。?
5. 声明该测试方法会抛出异常,这可以让代码适应性更强。有人可能会在测试程序外声明这个方法可抛出其他的异常,这种变化不应该影响你的测试,因此它不应该导致你的测试不能被编译。?
讨论:?
如果测试的方法抛出其他的异常---------不同于你要捕获的异常-----那么JUnit将报告一个error,而不是failure,因为测试方法将一个异常抛出给JUnit框架。记住这样的error一般是环境或者测试程序自身的错误,而不是产品代码的问题。如果产品代码抛出了一个预期之外的异常,那么一般可能是潜在的问题阻碍了测试的正常运行。?
一个更面向对象的解决办法?
先看一个匿名内部类?
Java代码??

虽然有人觉得Java的匿名内部类不太好读,但这个方法的意图再明显不过了:"测试这段代码是否抛出期望的异常"。可以按如下的方式实现assertThrows()方法:?
Java代码??

我们捕捉了所有的异常,而不仅是期望的异常,这是因为当编译的时候,我们还不知道代码会抛出什么样的异常。如果这样设置断言,那么错误信息就很重要,因为你取走了错误信息的控制权。另外一个可选的方案是,在assertThrows()方法中添加另外一个参数,用来接收自定义的异常,最后,因为我们必须测试所有的异常,我们必须将捕获的异常是否是期望的异常的实例,这与使用instanceof()方法是一样的。?
注意写的断言:?
如果你的断言与特定的异常相关,那么你要小心:如果验证异常对象的结构过于紧凑,那么可能导致测试与产品代码耦合得过强。这时候,测试的结果可能有点不太可靠。假设异常信息是给终端用户看,而你要写直接验证该消息的断言。一般来说,你会照着如下的方式编写测试代码:?
Java代码??

测试代码看上去很清楚:如果一个用户不输入用户名的情况下登录,那么登录模块就抛出一个MissingEntryException。这个异常包含了登录所缺少的必要项的名称,以及提供给终端用户的信息。这看起来很好,因为这个异常对象包含的信息非常简单,它包含的数据像终端用户读取的信息一样易于理解。虽然catch语句中的第一断言写得不错,但是第二个有点不同的意见,虽然userName是程序的内部名称,并且是行为的一部分,但给终端用户看的信息可以在不影响登录功能的前提下随时改变。换句话说,如果userName改变的话,测试程序就需要随之改变。然而,测试似乎不应该随着终端用户信息的改变而改变,由于测试时现在写的,将来任何属性值的改变,都会要求测试程序随之改变。?
这个例子中,我们建议去除第二个断言而仅保留第一个,因为涉及的一般准则是:将给终端用户看的信息与内部对象的行为相分离。你可以经常简单地初始化应该异常对象,然后检查它的toString()以及getMessage()方法的返回值。