簡體   English   中英

mockito使用間諜更好地預期異常測試

[英]mockito better expected exception test using spy

如何進行第3次測試以檢查異常消息中是否存在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);
    }
}

使用catch-exception庫,或者我猜你正在尋找的解決方案是你的第二個實現。

@expected沒有提供任何方法來斷言拋出的異常,除了它的類,所以你不能避免嘗試/捕獲(沒有那么多的樣板代碼!)

Mockito沒有提供類似verifyThrows方法的東西。

因此,您可以交換嘗試/捕獲其他庫:使用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'

如果A是你正在測試的系統,那么嘲笑它是沒有任何意義的,並且很少有人監視它。 您在testExpectedException2中的實現是正確的; 樣板代碼是必要的,因為沒有try塊,Java將不會在截獲方法后運行任何代碼(正如我在之前的SO回答中所述 )。

雖然Mockito沒有任何幫助,但JUnit會。 @Test(expected=foo)參數實際上有一個更靈活的替代方案,即內置的ExpectedException JUnit規則

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的消費者。 之后應該沒有什么可以在方法中進行測試,除了異常的屬性和MAYBE系統的狀態,這兩者都應該是稀疏的,以至於try / catch樣板是可以原諒的。

如果您有機會使用scala,scalaTest的有趣套件可以使用攔截( http://www.scalatest.org/getting_started_with_fun_suite )測試異常的簡潔方法。

這很簡單

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

如果您正在尋找易於編寫/清除測試,則可以將測試寫入scala中的java項目。 但這可能會帶來其他挑戰。

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

使用assertj-core-3.0.0 + Java 8 Lambdas

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

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

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

如果您在spy方法上查看Mockito.class,它會使用spiedInstance創建mock:

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

}

調用之后,您可以瀏覽報告並找到所需的報告,並驗證存儲的throwable是否為預期的。

例:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM