如何使用JUnit来测试某些代码是否抛出异常?

我当然可以这样做:

@Test
public void testFooThrowsIndexOutOfBoundsException() {
  boolean thrown = false;

  try {
    foo.doStuff();
  } catch (IndexOutOfBoundsException e) {
    thrown = true;
  }

  assertTrue(thrown);
}

我记得,对于这类情况,有一个注释或Assert.xyz之类的东西远没有JUnit那么笨拙,更符合JUnit的精神。


当前回答

在JUnit 4或更高版本中,您可以如下测试异常

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

这提供了许多可以用来改进JUnit测试的特性。如果您看到下面的示例,我将测试异常的3个方面。

引发的异常类型异常消息异常的原因

public class MyTest {

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

    ClassUnderTest classUnderTest;

    @Before
    public void setUp() throws Exception {
        classUnderTest = new ClassUnderTest();
    }

    @Test
    public void testAppleisSweetAndRed() throws Exception {

        exceptions.expect(Exception.class);
        exceptions.expectMessage("this is the exception message");
        exceptions.expectCause(Matchers.<Throwable>equalTo(exceptionCause));

        classUnderTest.methodUnderTest("param1", "param2");
    }

}

其他回答

这取决于JUnit版本和使用的断言库。

JUnit5和4.13见答案如果您使用AssertJ或google truth,请参阅答案

JUnit<=4.12的原始答案是:

    @Test(expected = IndexOutOfBoundsException.class)
    public void testIndexOutOfBoundsException() {

        ArrayList emptyList = new ArrayList();
        Object o = emptyList.get(0);

    }

尽管答案对于JUnit<=4.12有更多选项。

参考:

JUnit测试常见问题解答

只需制作一个可以关闭和打开的Matcher,如下所示:

public class ExceptionMatcher extends BaseMatcher<Throwable> {
    private boolean active = true;
    private Class<? extends Throwable> throwable;

    public ExceptionMatcher(Class<? extends Throwable> throwable) {
        this.throwable = throwable;
    }

    public void on() {
        this.active = true;
    }

    public void off() {
        this.active = false;
    }

    @Override
    public boolean matches(Object object) {
        return active && throwable.isAssignableFrom(object.getClass());
    }

    @Override
    public void describeTo(Description description) {
        description.appendText("not the covered exception type");
    }
}

要使用它:

add public ExpectedException exception=ExpectedException.none();,那么:

ExceptionMatcher exMatch = new ExceptionMatcher(MyException.class);
exception.expect(exMatch);
someObject.somethingThatThrowsMyException();
exMatch.off();

如前所述,JUnit中有许多处理异常的方法。但在Java8中还有另一个:使用Lambda表达式。使用Lambda表达式,我们可以实现如下语法:

@Test
public void verifiesTypeAndMessage() {
    assertThrown(new DummyService()::someMethod)
            .isInstanceOf(RuntimeException.class)
            .hasMessage("Runtime exception occurred")
            .hasMessageStartingWith("Runtime")
            .hasMessageEndingWith("occurred")
            .hasMessageContaining("exception")
            .hasNoCause();
}

assertThrown接受一个函数接口,它的实例可以用lambda表达式、方法引用或构造函数引用创建。assertThrown接受该接口将期望并准备好处理异常。

这是一种相对简单但功能强大的技术。

看看这篇描述这一技巧的博文:http://blog.codeleak.pl/2014/07/junit-testing-exception-with-java-8-and-lambda-expressions.html

源代码可在此处找到:https://github.com/kolorobot/unit-testing-demo/tree/master/src/test/java/com/github/kolorobot/exceptions/java8

披露:我是博客和项目的作者。

JUnit 5解决方案

import static org.junit.jupiter.api.Assertions.assertThrows;

@Test
void testFooThrowsIndexOutOfBoundsException() {    
  IndexOutOfBoundsException exception = expectThrows(IndexOutOfBoundsException.class, foo::doStuff);
     
  assertEquals("some message", exception.getMessage());
}

关于JUnit 5的更多信息http://junit.org/junit5/docs/current/user-guide/#writing-测试断言

我建议库assertj核心处理junit测试中的异常

在java 8中,如下所示:

//given

//when
Throwable throwable = catchThrowable(() -> anyService.anyMethod(object));

//then
AnyException anyException = (AnyException) throwable;
assertThat(anyException.getMessage()).isEqualTo("........");
assertThat(exception.getCode()).isEqualTo(".......);