我知道有一种方法是:

@Test
public void foo() {
   try {
      // execute code that you expect not to throw Exceptions.
   } catch(Exception e) {
      fail("Should not have thrown any exception");
   }
}

还有更干净的方法吗?(可能使用了Junit的@Rule?)


当前回答

用void方法来测试场景

void testMeWell() throws SomeException {..}

不抛出异常:

Junit5

assertDoesNotThrow(() -> {
    testMeWell();
});

其他回答

你想错了。只需测试您的功能:如果抛出异常,测试将自动失败。如果没有抛出异常,您的测试将全部显示为绿色。

我注意到这个问题有时会引起人们的兴趣,所以我将展开一点。

单元测试的背景

当您在进行单元测试时,为自己定义工作单元是很重要的。基本上:对代码库的提取,可能包含也可能不包含表示单个功能的多个方法或类。

或者,正如Roy Osherove在《单元测试的艺术》第二版第11页中定义的那样:

单元测试是一段自动的代码,它调用被测试的工作单元,然后检查关于该单元的单个最终结果的一些假设。单元测试几乎总是使用单元测试框架编写的。它易于编写,运行迅速。它是可靠的、可读的和可维护的。只要生产代码没有改变,它的结果是一致的。

重要的是要认识到一个工作单元通常不只是一个方法,但在最基本的层面上它是一个方法,之后它被其他工作单元封装。

理想情况下,您应该为每个单独的工作单元都有一个测试方法,这样您就可以立即查看哪里出了问题。在这个例子中,有一个名为getUserById()的基本方法,它将返回一个用户,总共有3个单元的工作。

第一个工作单元应该测试在有效和无效输入的情况下是否返回有效用户。 数据源抛出的任何异常都必须在这里处理:如果没有用户,则应该通过测试来证明在找不到用户时抛出了异常。这个例子可以是@Test(expected = IllegalArgumentException.class)注释捕获的IllegalArgumentException。

一旦您处理了这个基本工作单元的所有用例,您就可以提升一个级别。这里的操作与此完全相同,但是只处理来自当前级别下一层的异常。这使您的测试代码保持良好的结构,并允许您快速运行整个体系结构以找到问题所在,而不必到处跳来跳去。

处理测试的有效和错误输入

现在应该很清楚如何处理这些异常了。输入有两种类型:有效输入和错误输入(严格意义上的输入是有效的,但它不是正确的)。

当您使用有效输入时,您正在设置隐式期望,即无论您编写什么测试,都将工作。

这样的方法调用看起来像这样:existingUserById_ShouldReturn_UserObject。如果这个方法失败了(例如:抛出一个异常),那么你就知道出错了,可以开始挖掘了。

通过添加另一个使用错误输入并期望异常的测试(nonExistingUserById_ShouldThrow_IllegalArgumentException),您可以查看您的方法是否对错误输入执行了它应该执行的操作。

博士TL;

您试图在测试中做两件事:检查有效输入和错误输入。通过将它分成两个方法,每个方法做一件事,您将有更清晰的测试,并更好地了解哪里出了问题。

通过记住分层的工作单元,您还可以减少对层次结构中较高层次的层所需的测试量,因为您不必考虑较低层次中可能出错的每一件事:当前层以下的层是您的依赖项工作的虚拟保证,如果出现错误,它在当前层中(假设较低层次本身不抛出任何错误)。

如果您想测试您的测试目标是否使用异常。只需要将测试保留为(使用jMock2的模拟合作者):

@Test
public void consumesAndLogsExceptions() throws Exception {

    context.checking(new Expectations() {
        {
            oneOf(collaborator).doSth();
            will(throwException(new NullPointerException()));
        }
    });

    target.doSth();
 }

如果您的目标确实使用抛出的异常,则测试将通过,否则测试将失败。

如果您想测试异常使用逻辑,事情会变得更加复杂。我建议将消费委托给一个可能被嘲笑的合作者。因此,测试可以是:

@Test
public void consumesAndLogsExceptions() throws Exception {
    Exception e = new NullPointerException();
    context.checking(new Expectations() {
        {
            allowing(collaborator).doSth();
            will(throwException(e));

            oneOf(consumer).consume(e);
        }
    });

    target.doSth();
 }

但如果你只是想记录它,有时它就设计过度了。在这种情况下,如果您坚持使用tdd,本文(http://java.dzone.com/articles/monitoring-declarative-transac, http://blog.novoj.net/2008/09/20/testing-aspect-pointcuts-is-there-an-easy-way/)可能会有所帮助。

这可能不是最好的方法,但它肯定能确保不会从正在测试的代码块抛出异常。

import org.assertj.core.api.Assertions;
import org.junit.Test;

public class AssertionExample {

    @Test
    public void testNoException(){
        assertNoException();
    }    

    private void assertException(){
        Assertions.assertThatThrownBy(this::doNotThrowException).isInstanceOf(Exception.class);
    }

    private void assertNoException(){
        Assertions.assertThatThrownBy(() -> assertException()).isInstanceOf(AssertionError.class);
    }

    private void doNotThrowException(){
        //This method will never throw exception
    }
}

AssertJ可以处理这种情况:

assertThatNoException().isThrownBy(() -> System.out.println("OK"));

查看文档了解更多信息https://assertj.github.io/doc/#assertj-core-exception-assertions-no-exception

如果您不幸捕获了代码中的所有错误。 你可以愚蠢地做

class DumpTest {
    Exception ex;
    @Test
    public void testWhatEver() {
        try {
            thisShouldThrowError();
        } catch (Exception e) {
            ex = e;
        }
        assertEquals(null,ex);
    }
}