在JUnit中验证具体的异常详细信息

时间:2014-08-13 09:51:45

标签: java unit-testing junit

要验证异常消息我可以使用ExpectedMessage对象但是如果我想验证concreate异常及其详细信息怎么办?例如,我有异常类TerribleWindException,它有一些额外的方法,如getWindSpeed(),在junit测试方法中,我想检查getWindSpeed()结果。看看这个例子:

// pseudo implementation

public class TerribleWindException extends Exception {

    private Integer windSpeed = 0;

    public TerribleWindException(final Integer windSpeed) {
        this.windSpeed = windSpeed;
    }

}

public class WalkService {

    private Integer windSpeed = 0;

    public WalkService(final Integer windSpeed) {
        this.windSpeed = windSpeed;
    }

    public void goForAWalk() throws TerribleWindException {
        if  (windSpeed>10) {
            throw new TerribleWindException(windSpeed);
        }
    }

}

// test

public class WalkServiceTest {

    @Test
    public void testGoForAWalkWhenSpeedIsToPowerfulShouldThrowTerribleWindException throws TerribleWindException {
        WalkService ws = new WalkService(100);
        goForAWalk(); // this will throw TerribleWindException. The only way to check it's exception details is to use try {} catch() {} ?
    }

}

检查异常详细信息的唯一方法是使用try {} catch(){}?

8 个答案:

答案 0 :(得分:6)

您可以将JUnit' s ExpectedException rule与Hamcrest匹配器一起使用。

public class WalkServiceTest {
  @Rule
  public final ExpectedException thrown = ExpectedException.none();

  @Test
  public void testGoForAWalkWhenSpeedIsToPowerfulShouldThrowTerribleWindException throws TerribleWindException {
    WalkService ws = new WalkService(100);
    thrown.expect(TerribleWindException.class);
    thrown.expect(Matchers.hasProperty("windSpeed", Matchers.equalTo("expected speed")));
    ws.goForAWalk(); // this will throw TerribleWindException. The only way to check it's exception details is to use try {} catch() {} ?
  }
}

如果您正在使用Java 8,那么您可以将Vallado library与Hamcrest一起使用。

public class WalkServiceTest {
  @Test
  public void testGoForAWalkWhenSpeedIsToPowerfulShouldThrowTerribleWindException throws TerribleWindException {
    WalkService ws = new WalkService(100);
    when(() -> ws.goForAWalk())
      .thenA(TerribleWindException.class)
      .that(hasProperty("windSpeed", equalTo("expected speed")))
      .isThrown();
  }
}

答案 1 :(得分:2)

对于这种情况,我建议您使用Google的catch-exception库,代码示例(来自他们的主页):

import static com.googlecode.catchexception.CatchException.*;
import static com.googlecode.catchexception.apis.CatchExceptionBdd.*;

// given: an empty list
List myList = new ArrayList();

// when: we try to get the first element of the list
when(myList).get(1);

// then: we expect an IndexOutOfBoundsException
then(caughtException())
        .isInstanceOf(IndexOutOfBoundsException.class)
        .hasMessage("Index: 1, Size: 0") 
        .hasNoCause();

如果上面的示例还不够,请参阅catch-exception AssertJ api

答案 2 :(得分:1)

是。您可以使用@Test(expected=Exception.class)注释来测试抛出的异常类,但要测试详细信息,您需要自己捕获并查询异常。

如果没有抛出异常,请不要忘记fail()。 e.g。

try {
   operation();
   fail("Expected an exception");
}
catch (MyExpectedException e) {

   // assertions...
}

答案 3 :(得分:1)

如果您使用Java 8,实际上有一种更灵活的方式来实现您想要的,它使用Lambdas。 以下步骤是必要的:

  • 为您的例外
  • windSpeed - 属性创建一个getter
  • 为lambda创建一个函数接口,它不接受任何参数并且不返回任何内容,并且可能抛出异常,如下所示(Runnable不合适,因为它不会抛出异常而Callable有返回值):

    @FunctionalInterface
    public interface TestCommand {
        public void exec() throws Exception;
    }
    
  • 为异常创建自己的断言,如下所示:

    public <T extends Exception> void myAssertThrows(TestCommand testAction,
        Predicate<T> p) {
        try {
            testAction.exec();
            fail("must throw exception");
        } catch (Exception exception) {
            assertTrue(p.test((T) exception));
        }
    }
    
  • 在测试中使用它:

    @Test
    public void testGoForAWalkWhenSpeedIsToPowerfulShouldThrowTerribleWindException(){
        WalkService ws = new WalkService(10);
        myAssertThrows(() -> {
            ws.goForAWalk();
        }, (TerribleWindException e) -> e.getWindSpeed() == 100);
    }
    

那它是如何运作的? ws.goForAWalk();被打包成TestCommand类型的lambda,因此不会立即执行。它是myAssertThrows的第一个参数,并在此方法中执行。第二个参数是Exception的谓词,它只是一个lambda,它将一个异常作为参数,返回一个布尔值,可用于检查引发的异常属性。 assert方法使用着名的try-catch-style。

您实际上也可以在Java 7中使用assert方法,但调用的可读性较差,因为它需要匿名类。

答案 4 :(得分:0)

您可以使用Junit的规则Junit Rules,其中包含ExpectedException规则,该规则也可用于测试异常消息。

编辑:

如果您通过以下方式实现TerribleWindException:

public class TerribleWindException extends Exception {

    private Integer windSpeed = 0;

    public TerribleWindException(final Integer windSpeed) {
        // use Exception constructor, which passes a message
        super("windSpeed: " + windSpeed);
        this.windSpeed = windSpeed;
    }
}

您可以使用此测试代码实现您想要的目标:

@Rule
public ExpectedException thrown = ExpectedException.none();

@Test
public void shouldTestExceptionMessage() throws IndexOutOfBoundsException {
    List<Object> list = new ArrayList<Object>();

    thrown.expect(TerribleWindException .class);
    thrown.expectMessage("windSpeed: 100"); 
    WalkService ws = new WalkService(100);
    ws.goForAWalk(); 
}

答案 5 :(得分:0)

这个比我以前的答案更有意义,但我没有编辑我之前的答案,因为它适用于大多数用例。

ExpectedException规则允许您在测试中指定您期望的异常,甚至异常消息是什么

import static org.hamcrest.Matchers.*;
    import static org.junit.Assert.*;

    import org.junit.Rule;
    import org.junit.Test;
    import org.junit.rules.ExpectedException;

    public class CloudTest {

      @Rule
      public ExpectedException exception = ExpectedException.none();

      @Test
      public void testExpectedException() {
        exception.expect(CustomException.class);
        exception.expectMessage(containsString('Invalid cloud type'));
        new Cloud("Rainy","With thunder", "big");
      }
    }

答案 6 :(得分:-1)

我经常使用如下情况:

@Test
public void testGoForAWalkWhenSpeedIsToPowerfulShouldThrowTerribleWindException throws TerribleWindException {
    WalkService ws = new WalkService(100);
    try {
        goForAWalk();
        fail();
    catch(TerribleWindException twe) {
    }
}

答案 7 :(得分:-1)

不能通过使用以下模板来捕获特定的异常

@Test(expected= TerribleWindException.class)