Junit 在等待和处理异常后断言某些东西

Junit assert something after awaiting and handling an exception

在第一次和第二次调用时抛出的方法:

public void foo() throws Exception

测试:

@test
public void testFooThrowsAtFirstAndSecondTime(){
    boolean thrown;
    try {
       foo();
    } catch (Exception e) {
       thrown = true;
    }
    assertTrue(thrown);

    thrown = false;
    try {
       foo();
    } catch (Exception e) {
       thrown = true;
    }
    assertTrue(thrown);

    foo();
}

你能帮我找到更好的解决方案吗? 使用 Mockito 以获得更好的解决方案也是可以接受的。

更好的我的意思是,如果我可以在我的测试中避免 try/catch 甚至多个 try/catch。在其他语言或 jAssert 中,我认为即使在 spring 中也有这样的语句:

assertThrows(method(..)) //PseudoCode

我认为 Mockito 或 JUnit 4.x 有类似的事情。

我知道

@Test(expected=Exception)

但这只有在我期望抛出一次并且测试在那之后结束时才可以接受。

@Test(expected=Exception.class)

我认为每个方法调用一行是不可能的。

我会这样写测试:

@Test
public void testFooThrowsAtFirstAndSecondTime() throws Exception {
  try {
    foo();
    fail("foo did not throw an exception");
  } catch (Exception ex) { }

  try{
    foo(); 
    fail("foo did not throw an exception");
  } catch (Exception ex) { }

  foo();
}

通过 Java 8,您可以使用 Fishbowl 库。

@Test
public void testFooThrowsAtFirstAndSecondTime(){
  Throwable firstException = exceptionThrownBy(() -> foo());
  assertEquals(Exception.class, firstException.getClass());

  Throwable secondException = exceptionThrownBy(() -> foo());
  assertEquals(Exception.class, secondException.getClass());

  foo()
}

也可以将此库与 Java 6 和 7 一起使用。但是你必须使用匿名 类.

@Test
public void testFooThrowsAtFirstAndSecondTime(){
  Throwable firstException = exceptionThrownBy(new Statement() {
    public void evaluate() throws Throwable {
      foo();
    }
  });
  assertEquals(Exception.class, firstException.getClass());

  Throwable secondException = exceptionThrownBy(new Statement() {
    public void evaluate() throws Throwable {
      foo();
    }
  });
  assertEquals(Exception.class, secondException.getClass());

  foo()
}

如果您不幸不得不为 8 之前的某些版本的 java 编写代码,那么您将无法在每个例外中使用一行。

但如果您使用的是 java 8,那么您可以按照 Stefan Birkner 的建议进行操作。

现在,如果您不愿意只为一种方法包含整个库,那么这里有一个适合您的方法,已复制 from my blog

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();
        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 testFooThrowsAtFirstAndSecondTime()
{
    expectException( Exception.class, this::foo );
    expectException( Exception.class, this::foo );
    foo();
}

这里的关键是the try block is crucial如果你想在异常之后继续执行。您可以将它分解到方法或库中,但必须在您的测试方法中调用它。

有用的东西:

  • 行之有效的 fail() 成语,我更喜欢:

    try {
      foo();
      fail("foo did not throw an exception");
    } catch (Exception ex) { } 
    
  • catch-exception 是一个像 Mockito 一样的库,它包装传递的对象并在每个方法周围放置一个 try 块。 Mockito 关于 final 方法的警告和 类 也适用于此,因此这并不总是有效。

    List myList = new ArrayList();
    catchException(myList).get(1);  // myList is wrapped here
    assert caughtException() instanceof IndexOutOfBoundsException;
    

    请注意,catch-exception 处于“维护模式”,因为 Java 8 解决方案(下方)更加可靠。

  • 任何解决方案,例如 assertThrows(() -> methodThatThrows()) (Java 8) 或:

    assertThrows(new Runnable() {
      @Override public void run() { methodThatThrows(); }
    });
    

    ...在 Java 6/7 中。重要的是,assertThrows 在 methodThatThrows 之前被调用,因此它可以调用 methodThatThrows。感谢 Stefan ,但您可以轻松地自己编写一个等价物:

    public void assertThrows(Runnable block) {
      try {
        block.run();
        fail("Block didn't throw.");
      } catch (Exception ex) { }
    }
    

不起作用的东西:

  • @Test(expected=YourException.class) 将在堆栈中上升到 JUnit 将您的测试方法包装在其中的 try 块。在那之后永远不会 return 控制到测试方法.

  • JUnit4的ExpectedException@Rule看起来很诱人,但是因为它包装了整个测试方法,所以你必须在调用抛出异常的方法之前设置预期。

  • 任何看起来像 assertThrows(methodCallThatThrows()) 的东西。 Java 将尝试在调用 assertThrows 之前从 methodCallThatThrows 中获取 return 值,因此任何 try 不能帮助.