【问题标题】:Junit assert something after awaiting and handling an exceptionJunit 在等待和处理异常后断言某些东西
【发布时间】:2015-02-03 15:12:17
【问题描述】:

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

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 中,我认为即使在春天也有这样的陈述:

assertThrows(method(..)) //PseudoCode

我认为 Mockito 或 JUnit 4.x 也有类似的东西。

我知道

@Test(expected=Exception)

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

【问题讨论】:

  • 有什么问题?问题是什么?您究竟需要什么解决方案?
  • 在 jAssert 或其他语言中,有一些类似 assertThrow 的东西,我会发现它除了更具可读性之外,在某种程度上也是一种更正确的方法。

标签: java junit exception-handling mockito assert


【解决方案1】:

我不认为每次方法调用都只有一行。

我会这样写测试:

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

【讨论】:

  • 这也是一个有效的解决方案,但我正在寻找在我的测试中没有 try/catch 的东西。也许我要在另一个函数中重构 try/catch 例程......
【解决方案2】:

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

有效的方法:

  • 经过验证的fail() 成语you and nrainier cite,我更喜欢:

    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 的 pointing out Fishbowl,但您自己也可以轻松编写等效代码:

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

不起作用的事情:

  • @Test(expected=YourException.class) 将向上堆栈到 JUnit 将测试方法包装到其中的 try 块。此后控制权永远不会返回到测试方法。

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

  • 任何看起来像assertThrows(methodCallThatThrows()) 的东西。 Java 将尝试在assertThrows 被调用之前从methodCallThatThrows 中获取返回值,因此任何try 都会在那里阻塞无法帮助

【讨论】:

    【解决方案3】:

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

    【讨论】:

    • 我目前使用 Junit 4 和 Mockito (PowerMock),我发现在 Spring 或 jAssert 中有 assertThrow(..) 语句。但如果可能的话,我宁愿保留我当前的配置。我们也在开发 Java 7。
    【解决方案4】:

    如果你很不幸不得不为 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();
    }
    

    【讨论】:

      【解决方案5】:

      @Test(expected=Exception.class)

      【讨论】:

      • 那么,您的foo() 方法是否在第一次和第二次调用时抛出异常,并在第三次调用时无异常通过?我不知道foo() 做了什么,但如果你真的需要它,你的测试代码可以解决你的问题。 Mockito 解决方案会稍微难一些。还可以考虑重新设计您的 foo() 方法。
      • 那么,您的 foo() 方法是否在第一次和第二次调用时抛出异常,并在第三次调用时无异常通过?是的
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-15
      相关资源
      最近更新 更多