2171

如何以惯用方式使用 JUnit4 来测试某些代码是否引发异常?

虽然我当然可以做这样的事情:

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

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

  assertTrue(thrown);
}

我记得有一个注释或一个 Assert.xyz 或其他东西,在这种情况下,JUnit 的精神远不那么笨拙,而且更符合 JUnit 的精神。

4

34 回答 34

2492

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

的原始答案JUnit <= 4.12是:

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

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

}

虽然答案https://stackoverflow.com/a/31826781/2986984有更多 JUnit <= 4.12 的选项。

参考 :

于 2008-10-01T07:12:13.440 回答
1386

编辑:现在 JUnit 5 和 JUnit 4.13 已经发布,最好的选择是使用Assertions.assertThrows() (对于 JUnit 5)和Assert.assertThrows()(对于 JUnit 4.13+)。有关详细信息,请参阅我的其他答案

如果您还没有迁移到 JUnit 5,但可以使用 JUnit 4.7,则可以使用ExpectedExceptionRule:

public class FooTest {
  @Rule
  public final ExpectedException exception = ExpectedException.none();

  @Test
  public void doStuffThrowsIndexOutOfBoundsException() {
    Foo foo = new Foo();

    exception.expect(IndexOutOfBoundsException.class);
    foo.doStuff();
  }
}

这比因为如果之前抛出@Test(expected=IndexOutOfBoundsException.class)测试将失败要好得多IndexOutOfBoundsExceptionfoo.doStuff()

有关详细信息,请参阅本文

于 2010-05-29T17:16:23.680 回答
494

小心使用预期异常,因为它只断言该方法抛出了该异常,而不是测试中的特定代码行

我倾向于使用它来测试参数验证,因为这些方法通常非常简单,但更复杂的测试可能会更好地使用:

try {
    methodThatShouldThrow();
    fail( "My method didn't throw when I expected it to" );
} catch (MyException expectedException) {
}

应用判断。

于 2008-10-01T09:31:46.943 回答
219

如前所述,在 JUnit 中有多种处理异常的方法。但是在 Java 8 中还有另一个:使用 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

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

于 2014-07-07T22:35:19.753 回答
197

在junit中,有四种方法可以测试异常。

junit5.x

  • 对于junit5.x,您可以使用assertThrows如下

    @Test
    public void testFooThrowsIndexOutOfBoundsException() {
        Throwable exception = assertThrows(IndexOutOfBoundsException.class, () -> foo.doStuff());
        assertEquals("expected messages", exception.getMessage());
    }
    

junit4.x

  • 对于 junit4.x,使用 Test annonation 的可选 'expected' 属性

    @Test(expected = IndexOutOfBoundsException.class)
    public void testFooThrowsIndexOutOfBoundsException() {
        foo.doStuff();
    }
    
  • 对于 junit4.x,使用 ExpectedException 规则

    public class XxxTest {
        @Rule
        public ExpectedException thrown = ExpectedException.none();
    
        @Test
        public void testFooThrowsIndexOutOfBoundsException() {
            thrown.expect(IndexOutOfBoundsException.class)
            //you can test the exception message like
            thrown.expectMessage("expected messages");
            foo.doStuff();
        }
    }
    
  • 您还可以使用在 junit 3 框架下广泛使用的经典 try/catch 方式

    @Test
    public void testFooThrowsIndexOutOfBoundsException() {
        try {
            foo.doStuff();
            fail("expected exception was not occured.");
        } catch(IndexOutOfBoundsException e) {
            //if execution reaches here, 
            //it indicates this exception was occured.
            //so we need not handle it.
        }
    }
    
  • 所以

    • 如果你喜欢junit 5,那么你应该喜欢第一个
    • 当您只想测试异常类型时使用第二种方法
    • 当您想要进一步测试异常消息时使用第一个和最后两个
    • 如果您使用 junit 3,则首选第 4 个
  • 有关详细信息,您可以阅读此文档junit5 用户指南了解详细信息。

于 2015-08-05T08:05:50.707 回答
131

tl;博士

  • post-JDK8 :使用AssertJ或自定义 lambda 断言异常行为。

  • pre-JDK8 :我会推荐旧的好try-catch块。(不要忘记在块fail()之前添加一个断言catch

不管是 Junit 4 还是 JUnit 5。

长篇大论

可以自己编写一个自己动手 try-catch阻止或使用 JUnit 工具(@Test(expected = ...)@Rule ExpectedExceptionJUnit 规则功能)。

但是这些方法并不那么优雅,并且不能很好地将可读性与其他工具混合。此外,JUnit 工具确实存在一些缺陷。

  1. -块您必须围绕测试trycatch行为编写块并在 catch 块中写入断言,这可能很好,但许多人发现这种样式会中断测试的读取流程。此外,您需要Assert.fail在块的末尾写一个try。否则,测试可能会错过断言的一侧;PMDfindbugsSonar会发现此类问题。

  2. @Test(expected = ...)功能很有趣,因为您可以编写更少的代码,然后编写此测试据说不太容易出现编码错误。这种方法在某些领域是缺乏的。

    • 如果测试需要检查异常的其他内容,例如原因或消息(好的异常消息非常重要,拥有精确的异常类型可能还不够)。
    • 此外,由于期望被放置在方法中,根据测试代码的编写方式,测试代码的错误部分可能会引发异常,导致误报测试,我不确定PMDfindbugsSonar将提供有关此类代码的提示。

      @Test(expected = WantedException.class)
      public void call2_should_throw_a_WantedException__not_call1() {
          // init tested
          tested.call1(); // may throw a WantedException
      
          // call to be actually tested
          tested.call2(); // the call that is supposed to raise an exception
      }
      
  3. ExpectedException规则也是尝试修复之前的警告,但是使用起来感觉有点尴尬,因为它使用了期望样式,EasyMock用户非常了解这种样式。这对某些人来说可能很方便,但是如果您遵循行为驱动开发(BDD) 或安排行为断言(AAA) 原则,则该ExpectedException规则将不适合这些写作风格。除此之外,它可能会遇到与方式相同的问题@Test,具体取决于您将期望放在哪里。

    @Rule ExpectedException thrown = ExpectedException.none()
    
    @Test
    public void call2_should_throw_a_WantedException__not_call1() {
        // expectations
        thrown.expect(WantedException.class);
        thrown.expectMessage("boom");
    
        // init tested
        tested.call1(); // may throw a WantedException
    
        // call to be actually tested
        tested.call2(); // the call that is supposed to raise an exception
    }
    

    即使预期的异常放在测试语句之前,如果测试遵循 BDD 或 AAA,它也会破坏您的阅读流程。

    另外,请参阅ExpectedException. JUnit 4.13-beta-2甚至弃用了这种机制:

    拉取请求 #1519:弃用 ExpectedException

    Assert.assertThrows 方法提供了一种更好的方法来验证异常。此外,ExpectedException 的使用在与 TestWatcher 等其他规则一起使用时容易出错,因为在这种情况下规则的顺序很重要。

因此,上述这些选项有很多警告,显然不能避免编码错误。

  1. 在创建这个看起来很有希望的答案后,我意识到了一个项目,它是catch-exception

    正如该项目的描述所说,它让编码人员用流畅的代码行编写捕获异常并为后一个断言提供此异常。您可以使用任何断言库,例如HamcrestAssertJ

    取自主页的快速示例:

    // 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();
    

    如您所见,代码非常简单,您在特定行捕获异常,thenAPI 是一个别名,将使用 AssertJ API(类似于 using assertThat(ex).hasNoCause()...)。在某些时候,该项目依赖于 AssertJ 的祖先 FEST-Assert编辑:似乎该项目正在酝酿对 Java 8 Lambdas 的支持。

    目前,这个库有两个缺点:

    • 在撰写本文时,值得注意的是该库基于 Mockito 1.x,因为它在幕后创建了测试对象的模拟。由于 Mockito 仍未更新,因此此库无法与最终类或最终方法一起使用。即使它基于当前版本的 Mockito 2,这也需要声明一个全局模拟生成器 ( inline-mock-maker),这可能不是您想要的,因为这个模拟生成器与常规模拟生成器有不同的缺点。

    • 它需要另一个测试依赖项。

    一旦库支持 lambda,这些问题将不适用。但是,AssertJ 工具集将复制该功能。

    综合考虑如果不想使用catch-exception工具的话,我会推荐try-catch块的老好办法,至少到JDK7。对于 JDK 8 用户,您可能更喜欢使用 AssertJ,因为它提供的可能不仅仅是断言异常。

  2. 在 JDK8 中,lambda 进入了测试场景,事实证明它们是一种断言异常行为的有趣方式。AssertJ 已经更新,提供了一个很好的流畅的 API 来断言异常行为。

    以及使用AssertJ的示例测试:

    @Test
    public void test_exception_approach_1() {
        ...
        assertThatExceptionOfType(IOException.class)
                .isThrownBy(() -> someBadIOOperation())
                .withMessage("boom!"); 
    }
    
    @Test
    public void test_exception_approach_2() {
        ...
        assertThatThrownBy(() -> someBadIOOperation())
                .isInstanceOf(Exception.class)
                .hasMessageContaining("boom");
    }
    
    @Test
    public void test_exception_approach_3() {
        ...
        // when
        Throwable thrown = catchThrowable(() -> someBadIOOperation());
    
        // then
        assertThat(thrown).isInstanceOf(Exception.class)
                          .hasMessageContaining("boom");
    }
    
  3. 随着对 JUnit 5 的近乎完整的重写,断言得到了一些改进,它们可能被证明是一种有趣的开箱即用的方式来正确断言异常。但实际上断言 API 还是有点差,外面什么都没有assertThrows

    @Test
    @DisplayName("throws EmptyStackException when peeked")
    void throwsExceptionWhenPeeked() {
        Throwable t = assertThrows(EmptyStackException.class, () -> stack.peek());
    
        Assertions.assertEquals("...", t.getMessage());
    }
    

    正如您所注意到的,assertEquals它仍在返回void,因此不允许像 AssertJ 那样链接断言。

    此外,如果您记得名字与Matcheror发生冲突Assert,请准备好与 . 发生相同的冲突Assertions

我想得出结论,今天 (2017-03-03) AssertJ的易用性、可发现的 API、快速的开发速度以及作为事实上的测试依赖项是 JDK8 的最佳解决方案,无论测试框架如何(JUnit与否),以前的 JDK 应该改为依赖try-catch块,即使它们感觉很笨重。

这个答案是从另一个不具有相同可见性的问题中复制的,我是同一位作者。

于 2016-12-07T14:19:24.347 回答
80

现在 JUnit 5 和 JUnit 4.13 已经发布,最好的选择是使用Assertions.assertThrows() (for JUnit 5) 和Assert.assertThrows()(for JUnit 4.13)。请参阅JUnit 5 用户指南

这是一个验证抛出异常的示例,并使用Truth对异常消息进行断言:

public class FooTest {
  @Test
  public void doStuffThrowsIndexOutOfBoundsException() {
    Foo foo = new Foo();

    IndexOutOfBoundsException e = assertThrows(
        IndexOutOfBoundsException.class, foo::doStuff);

    assertThat(e).hasMessageThat().contains("woops!");
  }
}

与其他答案中的方法相比的优点是:

  1. 内置于 JUnit
  2. 如果 lambda 中的代码没有抛出异常,您会收到一条有用的异常消息,如果它抛出不同的异常,您会收到一个堆栈跟踪
  3. 简洁的
  4. 允许您的测试遵循 Arrange-Act-Assert
  5. 您可以准确地指出您希望抛​​出异常的代码
  6. 您不需要在throws子句中列出预期的异常
  7. 您可以使用您选择的断言框架对捕获的异常进行断言
于 2017-10-01T16:42:29.670 回答
44

怎么样:捕获一个非常普遍的异常,确保它脱离了 catch 块,然后断言异常的类是你所期望的。如果 a) 异常的类型错误(例如,如果您有一个空指针)并且 b) 从未抛出异常,则此断言将失败。

public void testFooThrowsIndexOutOfBoundsException() {
  Throwable e = null;

  try {
    foo.doStuff();
  } catch (Throwable ex) {
    e = ex;
  }

  assertTrue(e instanceof IndexOutOfBoundsException);
}
于 2008-10-01T07:03:01.870 回答
42

更新: JUnit5 对异常测试进行了改进:assertThrows.

以下示例来自:Junit 5 用户指南

@Test
void exceptionTesting() {
    IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
        throw new IllegalArgumentException("a message");
    });
    assertEquals("a message", exception.getMessage());
}

使用 JUnit 4 的原始答案。

有几种方法可以测试是否引发了异常。我还在我的帖子How to write great unit tests with JUnit中讨论了以下选项

设置expected参数@Test(expected = FileNotFoundException.class)

@Test(expected = FileNotFoundException.class) 
public void testReadFile() { 
    myClass.readFile("test.txt");
}

使用try catch

public void testReadFile() { 
    try {
        myClass.readFile("test.txt");
        fail("Expected a FileNotFoundException to be thrown");
    } catch (FileNotFoundException e) {
        assertThat(e.getMessage(), is("The file test.txt does not exist!"));
    }
     
}

ExpectedException规则测试。

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

@Test
public void testReadFile() throws FileNotFoundException {
    
    thrown.expect(FileNotFoundException.class);
    thrown.expectMessage(startsWith("The file test.txt"));
    myClass.readFile("test.txt");
}

您可以在 JUnit4 wiki 中阅读有关异常测试的更多信息,以获取 Exception testingbad.robot - Expecting Exceptions JUnit Rule

于 2017-01-10T01:29:43.843 回答
41

使用AssertJ断言,它可以与 JUnit 一起使用:

import static org.assertj.core.api.Assertions.*;

@Test
public void testFooThrowsIndexOutOfBoundsException() {
  Foo foo = new Foo();

  assertThatThrownBy(() -> foo.doStuff())
        .isInstanceOf(IndexOutOfBoundsException.class);
}

这比@Test(expected=IndexOutOfBoundsException.class)因为它保证测试中的预期行抛出异常并让您检查有关异常的更多详细信息(例如消息)更容易:

assertThatThrownBy(() ->
       {
         throw new Exception("boom!");
       })
    .isInstanceOf(Exception.class)
    .hasMessageContaining("boom");

此处的 Maven/Gradle 说明。

于 2016-03-05T11:07:24.873 回答
39

BDD风格解决方案:JUnit 4 + Catch Exception + AssertJ

import static com.googlecode.catchexception.apis.BDDCatchException.*;

@Test
public void testFooThrowsIndexOutOfBoundsException() {

    when(() -> foo.doStuff());

    then(caughtException()).isInstanceOf(IndexOutOfBoundsException.class);

}

依赖项

eu.codearte.catch-exception:catch-exception:2.0
于 2013-11-15T19:17:15.083 回答
33

为了解决同样的问题,我建立了一个小项目: http ://code.google.com/p/catch-exception/

使用这个小助手你会写

verifyException(foo, IndexOutOfBoundsException.class).doStuff();

这比 JUnit 4.7 的 ExpectedException 规则更简洁。与 skaffman 提供的解决方案相比,您可以指定您期望在哪一行代码中出现异常。我希望这有帮助。

于 2011-10-28T09:26:21.140 回答
22

你也可以这样做:

@Test
public void testFooThrowsIndexOutOfBoundsException() {
    try {
        foo.doStuff();
        assert false;
    } catch (IndexOutOfBoundsException e) {
        assert true;
    }
}
于 2013-05-07T17:17:18.507 回答
14

恕我直言,在 JUnit 中检查异常的最佳方法是 try/catch/fail/assert 模式:

// this try block should be as small as possible,
// as you want to make sure you only catch exceptions from your code
try {
    sut.doThing();
    fail(); // fail if this does not throw any exception
} catch(MyException e) { // only catch the exception you expect,
                         // otherwise you may catch an exception for a dependency unexpectedly
    // a strong assertion on the message, 
    // in case the exception comes from anywhere an unexpected line of code,
    // especially important if your checking IllegalArgumentExceptions
    assertEquals("the message I get", e.getMessage()); 
}

assertTrue某些人来说可能有点强,所以assertThat(e.getMessage(), containsString("the message");可能更可取。

于 2015-03-10T21:49:47.260 回答
13

JUnit 5 解决方案

@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-tests-assertions

于 2016-07-24T15:00:31.500 回答
13

我在Mkyong 博客中找到的 Junit 4 最灵活和优雅的答案。它具有try/catch使用@Rule注释的灵活性。我喜欢这种方法,因为您可以读取自定义异常的特定属性。

package com.mkyong;

import com.mkyong.examples.CustomerService;
import com.mkyong.examples.exception.NameNotFoundException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.hasProperty;

public class Exception3Test {

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

    @Test
    public void testNameNotFoundException() throws NameNotFoundException {

        //test specific type of exception
        thrown.expect(NameNotFoundException.class);

        //test message
        thrown.expectMessage(is("Name is empty!"));

        //test detail
        thrown.expect(hasProperty("errCode"));  //make sure getters n setters are defined.
        thrown.expect(hasProperty("errCode", is(666)));

        CustomerService cust = new CustomerService();
        cust.findByName("");

    }

}
于 2018-01-25T11:10:01.210 回答
12

我在这里尝试了很多方法,但它们要么很复杂,要么不太符合我的要求。事实上,可以很简单地编写一个辅助方法:

public class ExceptionAssertions {
    public static void assertException(BlastContainer blastContainer ) {
        boolean caughtException = false;
        try {
            blastContainer.test();
        } catch( Exception e ) {
            caughtException = true;
        }
        if( !caughtException ) {
            throw new AssertionFailedError("exception expected to be thrown, but was not");
        }
    }
    public static interface BlastContainer {
        public void test() throws Exception;
    }
}

像这样使用它:

assertException(new BlastContainer() {
    @Override
    public void test() throws Exception {
        doSomethingThatShouldExceptHere();
    }
});

零依赖:不需要mockito,不需要powermock;并且适用于最终课程。

于 2012-10-10T15:02:44.930 回答
11

JUnit 对此具有内置支持,具有“预期”属性

于 2008-10-01T07:05:49.260 回答
10

Java 8 解决方案

如果您想要一个解决方案:

  • 利用 Java 8 lambda
  • 依赖于任何 JUnit 魔法
  • 允许您在单个测试方法中检查多个异常
  • 检查测试方法中的一组特定行引发的异常,而不是整个测试方法中的任何未知行
  • 产生实际抛出的异常对象,以便您可以进一步检查它

这是我写的一个实用函数:

public final <T extends Throwable> T expectException( Class<T> exceptionClass, Runnable runnable )
{
    try
    {
        runnable.run();
    }
    catch( Throwable throwable )
    {
        if( throwable instanceof AssertionError && throwable.getCause() != null )
            throwable = throwable.getCause(); //allows testing for "assert x != null : new IllegalArgumentException();"
        assert exceptionClass.isInstance( throwable ) : throwable; //exception of the wrong kind was thrown.
        assert throwable.getClass() == exceptionClass : throwable; //exception thrown was a subclass, but not the exact class, expected.
        @SuppressWarnings( "unchecked" )
        T result = (T)throwable;
        return result;
    }
    assert false; //expected exception was not thrown.
    return null; //to keep the compiler happy.
}

取自我的博客

按如下方式使用它:

@Test
public void testMyFunction()
{
    RuntimeException e = expectException( RuntimeException.class, () -> 
        {
            myFunction();
        } );
    assert e.getMessage().equals( "I haz fail!" );
}

public void myFunction()
{
    throw new RuntimeException( "I haz fail!" );
}
于 2015-12-18T18:52:21.533 回答
9

就我而言,我总是从 db 获得 RuntimeException,但消息不同。并且异常需要分别处理。这是我测试它的方法:

@Test
public void testThrowsExceptionWhenWrongSku() {

    // Given
    String articleSimpleSku = "999-999";
    int amountOfTransactions = 1;
    Exception exception = null;

    // When
    try {
        createNInboundTransactionsForSku(amountOfTransactions, articleSimpleSku);
    } catch (RuntimeException e) {
        exception = e;
    }

    // Then
    shouldValidateThrowsExceptionWithMessage(exception, MESSAGE_NON_EXISTENT_SKU);
}

private void shouldValidateThrowsExceptionWithMessage(final Exception e, final String message) {
    assertNotNull(e);
    assertTrue(e.getMessage().contains(message));
}
于 2013-07-02T09:03:57.030 回答
6

只需制作一个可以关闭和打开的 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");
    }
}

要使用它:

添加public ExpectedException exception = ExpectedException.none();,然后:

ExceptionMatcher exMatch = new ExceptionMatcher(MyException.class);
exception.expect(exMatch);
someObject.somethingThatThrowsMyException();
exMatch.off();
于 2013-06-05T20:12:23.413 回答
6

我们可以在必须返回异常的方法之后使用断言失败:

try{
   methodThatThrowMyException();
   Assert.fail("MyException is not thrown !");
} catch (final Exception exception) {
   // Verify if the thrown exception is instance of MyException, otherwise throws an assert failure
   assertTrue(exception instanceof MyException, "An exception other than MyException is thrown !");
   // In case of verifying the error message
   MyException myException = (MyException) exception;
   assertEquals("EXPECTED ERROR MESSAGE", myException.getMessage());
}
于 2015-03-09T11:21:37.587 回答
6

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

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


这提供了许多可用于改进 JUnit 测试的功能。
如果您看到下面的示例,我正在对异常进行 3 项测试。

  1. 抛出的异常类型
  2. 异常消息
  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");
    }

}
于 2016-12-08T05:48:19.703 回答
5

除了NamShubWriter所说的,请确保:

  • ExpectedException 实例是公开的(相关问题
  • ExpectedException没有在 @Before 方法中实例化。这篇文章清楚地解释了 JUnit 执行顺序的所有复杂性。

要这样做

@Rule    
public ExpectedException expectedException;

@Before
public void setup()
{
    expectedException = ExpectedException.none();
}

最后,这篇博文清楚地说明了如何断言某个异常被抛出。

于 2015-05-22T19:09:24.103 回答
4

使用 Java8 的 Junit4 解决方案是使用此功能:

public Throwable assertThrows(Class<? extends Throwable> expectedException, java.util.concurrent.Callable<?> funky) {
    try {
        funky.call();
    } catch (Throwable e) {
        if (expectedException.isInstance(e)) {
            return e;
        }
        throw new AssertionError(
                String.format("Expected [%s] to be thrown, but was [%s]", expectedException, e));
    }
    throw new AssertionError(
            String.format("Expected [%s] to be thrown, but nothing was thrown.", expectedException));
}

那么用法是:

    assertThrows(ValidationException.class,
            () -> finalObject.checkSomething(null));

请注意,唯一的限制是final在 lambda 表达式中使用对象引用。该解决方案允许继续测试断言,而不是期望使用@Test(expected = IndexOutOfBoundsException.class)解决方案在方法级别上进行测试。

于 2018-04-06T15:52:07.077 回答
4

我推荐库assertj-core来处理junit测试中的异常

在 java 8 中,像这样:

//given

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

//then
AnyException anyException = (AnyException) throwable;
assertThat(anyException.getMessage()).isEqualTo("........");
assertThat(exception.getCode()).isEqualTo(".......);
于 2018-07-18T11:32:37.163 回答
2

JUnit框架有assertThrows()方法:

ArithmeticException exception = assertThrows(ArithmeticException.class, () ->
    calculator.divide(1, 0));
assertEquals("/ by zero", exception.getMessage());
于 2020-05-12T21:48:39.460 回答
1

举个例子,你想为下面提到的代码片段编写 Junit

public int divideByZeroDemo(int a,int b){

    return a/b;
}

public void exceptionWithMessage(String [] arr){

    throw new ArrayIndexOutOfBoundsException("Array is out of bound");
}

上面的代码是测试可能发生的一些未知异常,下面的代码是用自定义消息断言一些异常。

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

private Demo demo;
@Before
public void setup(){

    demo=new Demo();
}
@Test(expected=ArithmeticException.class)
public void testIfItThrowsAnyException() {

    demo.divideByZeroDemo(5, 0);

}

@Test
public void testExceptionWithMessage(){


    exception.expectMessage("Array is out of bound");
    exception.expect(ArrayIndexOutOfBoundsException.class);
    demo.exceptionWithMessage(new String[]{"This","is","a","demo"});
}
于 2016-10-29T07:34:18.383 回答
1

使用 Java 8,您可以创建一个方法,将代码检查和预期的异常作为参数:

private void expectException(Runnable r, Class<?> clazz) { 
    try {
      r.run();
      fail("Expected: " + clazz.getSimpleName() + " but not thrown");
    } catch (Exception e) {
      if (!clazz.isInstance(e)) fail("Expected: " + clazz.getSimpleName() + " but " + e.getClass().getSimpleName() + " found", e);
    }
  }

然后在你的测试中:

expectException(() -> list.sublist(0, 2).get(2), IndexOutOfBoundsException.class);

好处:

  • 不依赖任何库
  • 本地化检查 - 更精确,如果需要,允许在一个测试中拥有多个这样的断言
  • 便于使用
于 2017-10-01T12:03:38.217 回答
1
    @Test(expectedException=IndexOutOfBoundsException.class) 
    public void  testFooThrowsIndexOutOfBoundsException() throws Exception {
         doThrow(IndexOutOfBoundsException.class).when(foo).doStuff();  
         try {
             foo.doStuff(); 
            } catch (IndexOutOfBoundsException e) {
                       assertEquals(IndexOutOfBoundsException .class, ex.getCause().getClass());
                      throw e;

               }

    }

这是另一种检查方法是否抛出正确异常的方法。

于 2019-04-16T12:54:07.833 回答
0

我使用 Java 8 lambda 的解决方案:

public static <T extends Throwable> T assertThrows(Class<T> expected, ThrowingRunnable action) throws Throwable {
    try {
        action.run();
        Assert.fail("Did not throw expected " + expected.getSimpleName());
        return null; // never actually
    } catch (Throwable actual) {
        if (!expected.isAssignableFrom(actual.getClass())) { // runtime '!(actual instanceof expected)'
            System.err.println("Threw " + actual.getClass().getSimpleName() 
                               + ", which is not a subtype of expected " 
                               + expected.getSimpleName());
            throw actual; // throw the unexpected Throwable for maximum transparency
        } else {
            return (T) actual; // return the expected Throwable for further examination
        }
    }
}

您必须定义一个 FunctionalInterface,因为Runnable没有声明 required throws

@FunctionalInterface
public interface ThrowingRunnable {
    void run() throws Throwable;
}

该方法可以使用如下:

class CustomException extends Exception {
    public final String message;
    public CustomException(final String message) { this.message = message;}
}
CustomException e = assertThrows(CustomException.class, () -> {
    throw new CustomException("Lorem Ipsum");
});
assertEquals("Lorem Ipsum", e.message);
于 2017-10-04T11:06:42.287 回答
0

编写测试用例有两种方式

  1. 用方法抛出的异常注释测试。像这样的东西@Test(expected = IndexOutOfBoundsException.class)
  2. 您可以使用 try catch 块简单地捕获测试类中的异常,并对从测试类中的方法抛出的消息进行断言。

    try{
    }
    catch(exception to be thrown from method e)
    {
         assertEquals("message", e.getmessage());
    }
    

我希望这能回答您的问题 学习愉快...

于 2017-11-09T06:23:17.373 回答
-1

我想评论我对这个问题的解决方案,它避免了需要任何与异常相关的 JUnit 代码。

我将 assertTrue(boolean) 与 try/catch 结合使用来查找要抛出的预期异常。这是一个例子:

public void testConstructor() {
    boolean expectedExceptionThrown;
    try {
        // Call constructor with bad arguments
        double a = 1;
        double b = 2;
        double c = a + b; // In my example, this is an invalid option for c
        new Triangle(a, b, c);
        expectedExceptionThrown = false; // because it successfully constructed the object
    }
    catch(IllegalArgumentException e) {
        expectedExceptionThrown = true; // because I'm in this catch block
    }
    catch(Exception e) {
        expectedExceptionThrown = false; // because it threw an exception but not the one expected
    }
    assertTrue(expectedExceptionThrown);
}
于 2016-03-10T05:10:05.113 回答
-3
try {
    my method();
    fail( "This method must thrwo" );
} catch (Exception ex) {
    assertThat(ex.getMessage()).isEqual(myErrormsg);
}
于 2018-08-08T10:43:49.763 回答