提问



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


虽然我当然可以这样做:


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

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

  assertTrue(thrown);
}


我记得有一个注释或Assert.xyz或某些东西,对于这些种类的情况来说,它远没有那么愚蠢,而且更具有JUnit精神。

最佳参考


JUnit 4支持这个:


@Test(expected = IndexOutOfBoundsException.class)
public void testIndexOutOfBoundsException() {
    ArrayList emptyList = new ArrayList();
    Object o = emptyList.get(0);
}


参考:https://junit.org/junit4/faq.html#atests_7 [106]

其它参考1


编辑现在JUnit5已经发布,最好的选择是使用Assertions.assertThrows()(参见我的其他答案)。


如果你没有迁移到JUnit 5,但可以使用JUnit 4.7,你可以使用ExpectedException规则:[108]


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)要好得多,因为如果在foo.doStuff()之前抛出IndexOutOfBoundsException,测试将会失败


有关详细信息,请参阅此文章[109]

其它参考2


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


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


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


申请判决。

其它参考3


如前所述,有许多方法可以处理JUnit中的异常。但是对于Java 8,还有另外一个:使用Lambda Expressions。使用Lambda Expressions,我们可以实现如下语法:


@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 [110]


源代码可以在这里找到:https://github.com/kolorobot/unit-testing-demo/tree/master/src/test/java/com/github/kolorobot/exceptions/java8 [111]


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

其它参考4


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



  • 对于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.
        }
    }
    

  • 最后,对于junit5.x,您还可以使用assertThrows,如下所示


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

  • 所以



    • 当您只想测试异常类型时使用第一种方式

    • 当您想要进一步测试异常消息时,使用其他三种方法

    • 如果你使用junit 3,那么第三个是首选

    • 如果你喜欢junit 5,那么你应该喜欢第4个


  • 有关详细信息,请参阅此文档和junit5用户指南以获取详细信息。[112] [113]


其它参考5


TL;博士



  • JDK8之前:我会推荐旧的好try - catch块。

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



无论Junit 4还是JUnit 5。


长篇故事


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


但是这些方式并不那么优雅,并且不能与其他工具很好地混合可读性。



  1. try - catch块你必须围绕测试行为编写块,并在catch块中写下断言,这可能没什么问题,但很多人发现这种风格会中断读取流程测试你还需要在try块的末尾写一个Assert.fail,否则测试可能会错过断言的一面; PMD , findbugs 或 Sonar 会发现此类问题。

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



    • 如果测试需要检查异常上的其他内容,例如原因或消息(良好的异常消息非常重要,那么具有精确的异常类型可能还不够)。

    • 同样由于期望在方法中存在,取决于测试代码的编写方式,然后测试代码的错误部分会抛出异常,导致误报测试,我不确定 PMD , findbugs 或 Sonar 将提供有关此类代码的提示。


      @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上的这个评论问题。[114]



所以这些上面的选项都有他们的注意事项,显然不能免于编码器错误。



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


    正如项目描述所说的那样,它让编码器用流畅的代码行编写代码来捕获异常并为以后的断言提供此异常。你可以使用任何断言库,如Hamcrest或AssertJ。[116] [117]


    从主页获取的一个快速示例:


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


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


    目前这个库有两个缺点:



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

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



    一旦库支持lambdas,这些问题就不会适用,但AssertJ工具集会复制这些功能。


    考虑到如果你不想使用catch-exception工具,我将推荐try - catch块的旧方法,至少在JDK7之前。对于JDK 8用户,您可能更喜欢使用AssertJ,因为它提供的可能不仅仅是声明异常。

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


    并使用AssertJ进行样本测试:[118]


    @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之外什么都没有。[119] [120]


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


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


    此外,如果你记得与MatcherAssert的名字冲突,请准备好与Assertions相同的冲突。



我想总结一下今天(2017-03-03) AssertJ 的易用性,可发现的API,快速的开发速度以及事实上的测试依赖性对于JDK8而言,无论测试框架(JUnit与否)都是最好的解决方案,以前的JDK应该依赖于 try - catch 块,即使它们感觉很笨拙。


此答案已从另一个不具备相同知名度的问题中复制,我是同一作者。

其它参考6


BDD样式解决方案:JUnit 4 + Catch Exception + AssertJ



@Test
public void testFooThrowsIndexOutOfBoundsException() {

    when(foo).doStuff();

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

}


源代码




  • https://gist.github.com/mariuszs/23f4e1e1857c28449b61



依赖关系



eu.codearte.catch-exception:catch-exception:1.3.3

其它参考7


怎么样:捕获一个非常一般的异常,确保它使它脱离catch块,然后断言异常的类是你期望的那样。如果a)异常是错误的类型(例如,如果你有一个空指针)和b)异常没有被抛出,那么这个断言将失败。[122] [123] [124]]] [125] [126]


public void testFooThrowsIndexOutOfBoundsException() {
  Throwable e = null;

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

  assertTrue(e instanceof IndexOutOfBoundsException);
}

其它参考8


使用AssertJ断言,可以与JUnit一起使用:[127]


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说明。[128]

其它参考9


为了解决同样的问题,我确实设置了一个小项目:
http://code.google.com/p/catch-exception/[129]


使用这个小助手你会写


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


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

其它参考10


现在JUnit 5已经发布,最好的选择是使用Assertions.assertThrows()(参见
Junit 5用户指南)。[130]


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


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. 中列出预期的例外情况
  8. 您可以使用您选择的断言框架来对捕获的异常进行断言



类似的方法将添加到JUnit 4.13中的org.junit Assert中。

其它参考11


更新: JUnit5对异常测试有一个改进:assertThrows


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


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


使用JUnit 4的原始答案。


有几种方法可以测试抛出异常。我还在我的帖子中讨论了以下选项如何使用JUnit编写出色的单元测试[133]


设置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中阅读有关异常测试和bad.robot的异常测试的更多信息 - 期待异常JUnit规则。[134] [135]

其它参考12


你也可以这样做:


@Test
public void testFooThrowsIndexOutOfBoundsException() {
    try {
        foo.doStuff();
        assert false;
    } catch (IndexOutOfBoundsException e) {
        assert true;
    }
}

其它参考13


恕我直言,在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");可能更好。

其它参考14


JUnit 5解决方案



@Test
void testFooThrowsIndexOutOfBoundsException() {    
  Throwable exception = expectThrows( IndexOutOfBoundsException.class, foo::doStuff );

  assertEquals( "some message", exception.getMessage() );
}


有关JUnit 5的更多信息,请访问http://junit.org/junit5/docs/current/user-guide/#writing-tests-assertions [136]

其它参考15


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


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;和最后的课程一起工作得很好。

其它参考16


JUnit内置了对此的支持,具有预期属性[137]

其它参考17


Java 8解决方案



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



  • 使用Java 8 lambdas

  • 不依赖于任何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 "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.
}


(取自我的博客)[138]


使用方法如下:


@Test
public void testThrows()
{
    RuntimeException e = expectException( RuntimeException.class, () -> 
        {
            throw new RuntimeException( "fail!" );
        } );
    assert e.getMessage().equals( "fail!" );
}

其它参考18


在我的情况下,我总是从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));
}

其它参考19


在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");
    }

}

其它参考20


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


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

其它参考21


我在Mkyoung博客中找到的Junit 4最灵活,最优雅的答案。它具有使用@Rule注释的try/catch的灵活性。我喜欢这种方法,因为我已经需要阅读自定义异常的特定属性。[139]


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

    }

}

其它参考22


除了NamShubWriter所说的,请确保:



  • ExpectedException实例是公开(相关问题)

  • ExpectedException 不是实例化,比如@Before方法。这篇文章清楚地解释了JUnit执行顺序的所有复杂性。



执行此操作: [142]


@Rule    
public ExpectedException expectedException;

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


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

其它参考23


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


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

其它参考24


例如,您想为下面提到的代码片段编写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"});
}

其它参考25


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


[[43.]]

然后在你的测试中:


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


优点:



  • 不依赖任何图书馆

  • 本地化检查 - 更精确,并允许在一次测试中有多个这样的断言(如果需要)

  • 易于使用


其它参考26


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


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没有声明所需的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);

其它参考27


编写测试用例有两种方法



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


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



我希望这能回答你的疑问
快乐学习......

其它参考28


使用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));


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

其它参考29


我建议库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(".......);