对异常的验证是单元测试中一个很重要的环节。
是不是程序在测试过程中抛出异常,就是bug呢。恰恰相反,在该抛出异常的地方,一定要抛异常,如果没有抛,反而是bug。
在测试之前,我们需要熟读设计文档,弄清楚程序在什么情况下,会抛出什么异常。举个例子,比如有一个方法bool InsertUser(),如果插入成功,返回true,如果由于所插入的用户信息有问题(比如缺少必填的name),是返回false呢,还是直接抛出异常。对于这两种情况,测试代码是完全不同的,所以我们必须先搞清楚程序是如何设计的。
在一般的程序中经常会用try、catch的方式,增强程序的健壮性,但是在测试程序里面,try确要慎用,绝对不能在每个地方都使用try,这是为什么呢?
还是上面那个例子,当插入的用户信息有错时,如果程序会抛出异常,那么我们的测试程序才能使用try,如果返回的是false,那我们就不能使用try。如果这时使用了try,而insertUser出了问题,那测试程序还是会显示通过,bug就被漏了。
程序中定义的异常通常都是从Exception这个基类继承的,如果我们在测试代码中使用try,却不能直接catch
Exception,而是要指定程序抛出的异常类型,下面是一段正确的测试代码。
try{
us.insertUserExtraInfo( user );
fail("错误的参数,没有抛出异常");
}catch(RemoteAccessException e){
//验证异常信息是否正确的代码
}
这里的RemoteAccessException就是程序定义的类型,我们catch的时候一定要指定这个类型,如果我们catch
Exception,那么当程序抛出其他错误类型的异常时,我们就把这个bug漏了。
当成功catch到异常以后,我们还需要对异常的具体信息进行验证。比如,插入的用户name没有填,异常信息应该是“用户信息错误”,如果插入一个已经存在的用户,那么信息就是“用户重复”等等。我们在测试程序中catch住异常以后,就需要对这些message进行检查,确保和我们期望的一样。如果不检查,当插入一个已经存在的用户时,异常信息是“未指定错误”,那bug就漏了。
最后说一下异常信息的验证,通常情况下我们比对两个String是否一致就行了,但是有时候,信息中包含了一些变化的信息,如果每次变化的时候,我们都要改代码,那就太麻烦了。建议使用正则表达式,只验证message中的关键部分。
举个例子,有一个异常信息是“程序错误 [192.168.1.1:1234] 用户已经存在”,这里IP的信息有可能变化,那我们就定义一个正则表达式
程序错误 \\[[\\d.]+:[\\d]+\\] 用户已经存在$
然后使用下面的代码进行验证
Pattern.compile(正则表达式).matcher(实际抛出的异常信息).find();