`
jiangduxi
  • 浏览: 463813 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

测试是否抛出正确的异常(Test throwing the right exception)

阅读更多
问题:
    你是否想过异常也要去测试?你想了怎么验证一个方法是否在某种特定的情况下抛出期望的异常,也许你正在找是否要测试,以及有没有简单的测试方法

背景:
    要想知道任何实现这种测试,你需要了解JUnit如何判定一个测试时通过还是失败。如果一个断言失败或者抛出一个异常的时候,测试就会失败,否则测试就通过。换句话说,如果一个测试全部走完,就是说程序从头运行到了尾,而没有从中间跳过,那么它就通过了。知道了这些,就足够你推断出然后写这种测试了:如果该抛出异常的代码段没有抛出异常,那么这个测试就应该失败;测试只能捕捉期望的异常;任何其他的异常都应该由JUnit框架捕捉。

诀窍:
   下面的代码展示了,如何写这种验证是否抛出正确异常的测试示例:
  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());
        }
  }

首先分析上面的代码
1. 找到可能抛出异常的代码段,将它放入一个try语句内。
2. 调用了应该抛出异常的方法以后,在try语句内写一个fail()方法来说明:“如果运行到了这里,那么说明期望的异常没有被抛出”。
3. 添加一个catch语句以捕获期望的异常。
4. 在catch语句内,如果需要的话,验证捕获的异常的属性与你期望的相同。
5. 声明该测试方法会抛出异常,这可以让代码适应性更强。有人可能会在测试程序外声明这个方法可抛出其他的异常,这种变化不应该影响你的测试,因此它不应该导致你的测试不能被编译。

讨论:
如果测试的方法抛出其他的异常---------不同于你要捕获的异常-----那么JUnit将报告一个error,而不是failure,因为测试方法将一个异常抛出给JUnit框架。记住这样的error一般是环境或者测试程序自身的错误,而不是产品代码的问题。如果产品代码抛出了一个预期之外的异常,那么一般可能是潜在的问题阻碍了测试的正常运行。

一个更面向对象的解决办法
先看一个匿名内部类
    public void testForException(){
      assertThrows(MyException.class, new ExceptionClosure(){
         public Object execute(Object input) throws Exception{
              return doSomethingThatShouldThrowMyException();
             }
        });
      }
 

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

我们捕捉了所有的异常,而不仅是期望的异常,这是因为当编译的时候,我们还不知道代码会抛出什么样的异常。如果这样设置断言,那么错误信息就很重要,因为你取走了错误信息的控制权。另外一个可选的方案是,在assertThrows()方法中添加另外一个参数,用来接收自定义的异常,最后,因为我们必须测试所有的异常,我们必须将捕获的异常是否是期望的异常的实例,这与使用instanceof()方法是一样的。

注意写的断言:
如果你的断言与特定的异常相关,那么你要小心:如果验证异常对象的结构过于紧凑,那么可能导致测试与产品代码耦合得过强。这时候,测试的结果可能有点不太可靠。假设异常信息是给终端用户看,而你要写直接验证该消息的断言。一般来说,你会照着如下的方式编写测试代码:
  public void testNameNoEntered(){
   try{
       log("", "password");
       fail("User logged in without a user name !");
       }
 catch(MissingEntryException expected){
      assertEquals("userName", expected.getEnteryName());
      assertEquals("Please enter a user name and try again",
    expected.getMessage());
  
       }
  }

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

这个例子中,我们建议去除第二个断言而仅保留第一个,因为涉及的一般准则是:将给终端用户看的信息与内部对象的行为相分离。你可以经常简单地初始化应该异常对象,然后检查它的toString()以及getMessage()方法的返回值。
分享到:
评论

相关推荐

    aop+exception的测试代码

    4. **编写测试方法**:在测试类中,创建一个会抛出异常的方法,并使用断言确保AOP通知被正确执行。 总结一下,"aop+exception"的测试代码主要涉及Spring AOP中的异常处理,可能涵盖了定义切面、通知、事务管理和...

    java 异常处理ppt

    `try`块包含可能会抛出异常的代码,而`catch`块则包含处理这些异常的代码。如果在`try`块中发生异常,控制流会立即跳到相应的`catch`块,而不是继续执行`try`块后面的代码。每个`catch`块都有一个特定的异常类型作为...

    java面试的常见问题

    - **抛出异常(Throwing Exceptions)**:通过`throw`关键字来显式地抛出异常。 - **捕获异常(Handling Exceptions)**:通过`try-catch`块来捕获并处理异常。 - **自定义异常(Custom Exceptions)**:允许程序员...

    java的基础相关笔记

    ##### 抛出异常(Throwing Exceptions) - **throw**:用来抛出一个异常对象,通常是在方法内部遇到某种错误情况时调用。 - **throws**:用来声明一个方法可能抛出的异常,它位于方法签名之后。 ##### 异常层次...

    Spring aop.rar

    - 异常后通知(After Throwing Advice):在方法抛出异常后执行。 - 最终通知(After(Finally)Advice):无论方法是否正常结束都会执行。 - 环绕通知(Around Advice):完全包围一个方法调用,可以在方法调用...

    Spring AOP配置源码

    以上结合起来意思就是在调用com.spring.service包或子包下的所有方法之前或之后或抛出异常时依次调用id为logIntercepter的类中的before after exception方法 测试用例 package com.spring.test; import javax....

    spring aop API示例

    需要注意的是,即使有异常抛出,After通知也会执行。 3. **AfterThrowing通知**: 当方法抛出异常时,AfterThrowing通知会被调用。`@AfterThrowing`注解允许我们指定只有在特定异常抛出时才执行的通知。这在进行...

    SpringAOP中的注解配置详解

    * @AfterThrowing注解:在切入点上的方法执行期间抛出异常的时候,会调用这个注解所标注的方法 ```java @AfterThrowing(value="myPointCut()", throwing="ex") public void throwingTest(JoinPoint p, Exception ex...

Global site tag (gtag.js) - Google Analytics