6

如何进行第三次测试以检查异常消息中是否存在 cause1?我还在前两个测试中列出了缺点。首先是不检查消息,其次需要大量样板代码。

public class CheckExceptionsWithMockitoTest {

    @Test(expected = RuntimeException.class)
    public void testExpectedException1() {
        A a = new A();
        a.doSomethingThatThrows();
    }

    @Test
    public void testExpectedException2() {
        A a = new A();
        try {
            a.doSomethingThatThrows();
            fail("no exception thrown");
        } catch (RuntimeException e) {
            assertThat(e.getMessage(), org.hamcrest.Matchers.containsString("cause1"));
        }
    }

    @Test
    public void testExpectedException3() {
        A a = new A();
        A spyA = org.mockito.Mockito.spy(a);
        // valid but doesnt work
        // doThrow(new IllegalArgumentException()).when(spyA).doSomethingThatThrows();
        // invalid but in the spirit of what i want 
        //chekThrow(RuntimeException.class,containsString("cause1")).when(spyA).doSomethingThatThrows();
    }

}

我在 Mockito 中找不到有效的东西,但有些东西看起来是可能的(在语法层面上)和功能。


使用 catchexception 我创建了这样的测试

import static com.googlecode.catchexception.CatchException.*;
import static com.googlecode.catchexception.apis.CatchExceptionHamcrestMatchers.*;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;

import org.junit.*;
public class CheckExceptionsWithMockitoTest{  
    //...
    @Test
    public void testExpectedException3() {
        A a = new A();
        verifyException(a,IllegalArgumentException.class)
            .doSomethingThatThrows();
        //if more details to be analized are needed
        assertThat(
            (IllegalStateException) caughtException(),
            allOf(
                is(IllegalStateException.class),
                hasMessageThat(
                        containsString("is not allowed to add counterparties")), 
                hasNoCause()));
        //more asserts could come
        assertNotNull(a);
    }
}
4

6 回答 6

5

使用捕获异常库,或者我猜您正在寻找的解决方案是您的第二个实现。

@expected 没有提供任何方法来断言抛出的异常,除了它的类,所以你不能避免尝试/捕获(没有那么多样板代码!)

Mockito 不提供类似于verifyThrows方法的东西。

因此,您可以用 try/catch 换取额外的库:使用catch-exception,您将能够在一行中捕获异常,并为进一步的断言做好准备。

示例源代码

A a = new A();

when(a).doSomethingThatThrows();

then(caughtException())
        .isInstanceOf(IllegalStateException.class)
        .hasMessageContaining("is not allowed to add counterparties")
        .hasNoCause();

依赖项

'com.googlecode.catch-exception:catch-exception:1.2.0'
于 2013-07-22T11:21:07.800 回答
4

如果A您的系统正在测试中,那么模拟它没有任何意义,监视它也几乎没有意义。您的实施testExpectedException2是正确的;样板代码是必要的,因为没有try块 Java 不会让任何代码在方法被拦截后运行(正如我在之前的 SO 答案中所描述的那样)。

尽管 Mockito 不会有任何帮助,但 JUnit 会。该@Test(expected=foo)参数实际上有一个更灵活的替代方案,即内置的ExpectedExceptionJUnit 规则

public class CheckExceptionsWithMockitoTest {

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

  @Test
  public void testExpectedException1() {
    A a = new A();
    thrown.expect(RuntimeException.class);
    thrown.expectMessage(containsString("cause1"));
    a.doSomethingThatThrows();
  }
}

Mockito在单独的测试中派上用场,检查您的方法是否包装了任意异常同时保留其消息,大致如下所示:

@Test
public void doSomethingShouldWrapExceptionWithPassedMessage() {
  Dependency dependency = Mockito.mock(Dependency.class);
  when(dependency.call()).thenThrow(new IllegalArgumentException("quux"));
  A a = new A(dependency);
  thrown.expect(RuntimeException.class);
  thrown.expectMessage(containsString("quux"));
  a.doSomethingThatThrows();
}

小心避免将其作为测试中的常见模式。如果您从被测系统中捕获异常,您实际上是在将控制权交还给 SUT 的使用者。之后在方法中应该几乎没有什么可以测试的了,除了异常的属性和可能是系统的状态,这两者都应该很少见,以至于 try/catch 样板是可以原谅的。

于 2013-07-21T23:49:56.303 回答
1

如果你有机会使用 scala,scalaTest 的 fun 套件有简洁的方法使用拦截(http://www.scalatest.org/getting_started_with_fun_suite)测试异常。

这很简单

  test(a list get method catches exceptions){
    intercept[IndexOutBoundsException]{
      spyListObject.get(-1)
    }
  }

如果您正在寻找易于编写/清晰的测试,您可能会将您的测试写入 scala 中的 java 项目。但这可能会带来其他挑战。

于 2014-12-04T17:46:35.690 回答
1

2015 年 6 月 19 日的更新答案(如果您使用的是 java 8)

使用 assertj-core-3.0.0 + Java 8 Lambda

@Test
public void shouldThrowIllegalArgumentExceptionWhenPassingBadArg() {
      assertThatThrownBy(() -> myService.sumTingWong("badArg"))
                                  .isInstanceOf(IllegalArgumentException.class);
}

参考: http: //blog.codeleak.pl/2015/04/junit-testing-exceptions-with-java-8.html

于 2015-06-19T05:21:18.450 回答
0

使用 catchexception 我创建了这样的测试

import static com.googlecode.catchexception.CatchException.*;
import static com.googlecode.catchexception.apis.CatchExceptionHamcrestMatchers.*;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;

import org.junit.*;
public class CheckExceptionsWithMockitoTest{  
    //...
    @Test
    public void testExpectedException3() {
        A a = new A();
        verifyException(a,IllegalArgumentException.class)
            .doSomethingThatThrows();
        //if more details to be analized are needed
        assertThat(
            (IllegalStateException) caughtException(),
            allOf(
                is(IllegalStateException.class),
                hasMessageThat(
                        containsString("is not allowed to add counterparties")), 
                hasNoCause()));
        //more asserts could come
        assertNotNull(a);
    }
}
于 2013-07-28T15:33:28.123 回答
0

如果您查看 Mockito.class on spy 方法,它会使用 spiedInstance 创建模拟:

 public static <T> T spy(T object) {
    return MOCKITO_CORE.mock((Class<T>) object.getClass(), withSettings()
            .spiedInstance(object)
            .defaultAnswer(CALLS_REAL_METHODS));
}

在 MockSettings 中可以注册调用监听器:https ://static.javadoc.io/org.mockito/mockito-core/3.0.0/org/mockito/listeners/InvocationListener.html

我创建了存储所有报告调用的简单侦听器:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.mockito.listeners.InvocationListener;
import org.mockito.listeners.MethodInvocationReport;

public class StoringMethodInvocationListener implements InvocationListener {

private List<MethodInvocationReport> methodInvocationReports = new ArrayList<>();

@Override
public void reportInvocation(MethodInvocationReport methodInvocationReport) {
    this.methodInvocationReports.add(methodInvocationReport);

}

public List<MethodInvocationReport> getMethodInvocationReports() {
    return Collections.unmodifiableList(methodInvocationReports);
}

}

调用后,您可以查看报告并找到所需的报告,并验证存储的可抛出对象是预期的。

例子:

    StoringMethodInvocationListener listener = new StoringMethodInvocationListener();
    Consumer mock2 = mock(Consumer.class, withSettings()
            .spiedInstance(consumerInstance)
            .defaultAnswer(CALLS_REAL_METHODS)
            .invocationListeners(listener));

    try {
        mock2.listen(new ConsumerRecord<String, String>(RECEIVER_TOPIC, 0, 0,  null, "{}"));
    } catch (Exception e){
        //nothing
    }
    Assert.notEmpty(listener.getMethodInvocationReports(), "MethodInvocationReports list must not be empty");
    Assert.isInstanceOf(BindException.class, listener.getMethodInvocationReports().get(1).getThrowable());
于 2019-09-11T12:03:44.877 回答