Java - Mockitoを利用して、テストコードの作成方法

Mockitoは、Javaで人気のあるMocking frameworkです。 MockitoでオブジェクトをmockingてUnit Testを作成できます。

直接Mockオブジェクトを作成することができますがMockitoと同じMocking frameworkを使用すると、面倒なコードを書く必要がなくなります。

この記事では、Mockitoにどのようにテストコードを作成するかを知ってみましょう。

  • Mocking
  • Verify
  • ArgumentCaptor
  • Spying
  • Exception足

依存性の設定

gradleプロジェクトでは、次のように依存性を設定すると、JUnitとMockitoライブラリを使用することができます。

dependencies {
    testImplementation 'junit:junit:4.12'
    testImplementation 'org.mockito:mockito-core:2.7.22'
}

Mocking

次のように mock(ClassToMock.class)でMockオブジェクトを作成することができます。

Mockオブジェクトを作成すると、内部のコードはすべて動作しておらず、戻り値は0、false、nullなどで置き換えられます。

@Test
public void mockingList() {
    List mockList = mock(ArrayList.class);

    mockList.add("apple");
    assertEquals("apple", mockList.get(0));
}

上記のコードを実行してみると、nullが返され、テストが失敗します。

java.lang.AssertionError:
Expected :apple
Actual   :null

When

whenを使用すると、どのような状況ではMockオブジェクトがどのような値を返すようにすることができます。 これStubbing呼ばれます。

次のコードを解釈すると mockListオブジェクトがget(0)を呼び出すときに、"appple"を返しなさいということです。

when(mockList.get(0)).thenReturn("apple");

次のように get()size()が呼び出されると、特定の値が設定されるようにしました。

@Test
public void mockingList_when() {
    List mockList = mock(ArrayList.class);

    when(mockList.get(0)).thenReturn("apple");
    when(mockList.get(1)).thenReturn("kiwi");
    when(mockList.size()).thenReturn(10);

    assertEquals("apple", mockList.get(0));
    assertEquals("kiwi", mockList.get(1));
    assertEquals(10, mockList.size());
}

入力された引数とは無関係に特定の値を返すしたい場合は anyInt()を使用します。 このほか、 anyString()anyLong()など、すべてのタイプのメソッドが提供されます。

@Test
public void mockingList_when2() {
    List mockList = mock(ArrayList.class);

    when(mockList.get(anyInt())).thenReturn("apple");
    assertEquals("apple", mockList.get(0));
    assertEquals("apple", mockList.get(1));
}

Verify

verifyを利用すれば、mockオブジェクトにどのようなAPIが呼び出されたか、何度呼び出したことを確認することができます。

次のコードは、 mockList.add("apple")が呼び出されたことを確認します。もし呼び出されていない場合、テストは失敗します。

verify(mockList).add("apple");

次のコードは、 mockList.get(0)が二度呼び出されたことを確認します。 times()の引数に予想される呼び出し回数を転送します。

verify(mockList, times(2)).get(0);

だから、次のようにメソッドが呼び出されたかを確認することができます。 atLeastOnce()atLeast()times()などのメソッドを提供してくれ何度呼び出していることを確認することができます。

@Test
public void mockingList_verify() {
    List mockList = mock(ArrayList.class);

    mockList.add("apple");
    verify(mockList).add("apple");

    mockList.get(0);
    mockList.get(0);
    verify(mockList, times(2)).get(0);
    verify(mockList, atLeast(2)).get(0);
}

ArgumentCaptor

ArgumentCaptorはMockオブジェクトに渡された引数を確認する目的で使用します。

次のコードは、 mockList.add()の引数を読み込むコードです。 verifyに capture()を渡して getValue()でどんな値が渡されていることを確認することができます。

ArgumentCaptor<String> arg = ArgumentCaptor.forClass(String.class);
verify(mockList).add(arg.capture());
assertEquals("apple", arg.getValue());

次のようにArgumentCaptorを利用して、 add()のいくつかの因子が配信された確認できます。

@Test
public void captorExample() {
    ArgumentCaptor<String> arg = ArgumentCaptor.forClass(String.class);

    mockList.add("apple");
    verify(mockList).add(arg.capture());

    assertEquals("apple", arg.getValue());
}

Spying

Mockオブジェクトは、内部実装が動作せず0、false、nullなどが返されるとしたんです。 Spyオブジェクトは、実際のオブジェクトのように動作し、verifyにどのメソッドが呼び出されたかを確認することができます。 また、whenキーワードで、いくつかのメソッドをstubbingすることができます。

次のようにspyオブジェクトを作成することができます。実行してみると、テストはpassており、実際のオブジェクトのように動作することがわかります。

@Test
public void spyingList() {
    List spyList = spy(ArrayList.class);

    spyList.add("apple");
    assertEquals("apple", spyList.get(0));
}

次のように verifyでメソッドが呼び出された検証することができます。

@Test
public void spyingList_verify() {
    List spyList = spy(ArrayList.class);

    spyList.add("apple");
    verify(spyList).add("apple");

    spyList.get(0);
    verify(spyList).get(0);
}

さらに、次のように whenに、いくつかのメソッドをstubbingすることができます。しかし、次のコードは、問題があります。

@Test
public void spyingList_when_error() {
    List spyList = spy(ArrayList.class);

    when(spyList.get(0)).thenReturn("apple");
    when(spyList.get(1)).thenReturn("kiwi");
    when(spyList.size()).thenReturn(10);

    assertEquals("apple", spyList.get(0));
    assertEquals("kiwi", spyList.get(1));
    assertEquals(10, spyList.size());
}

上記のコードを実行すると、 IndexOutOfBoundsException例外が発生し、テストは失敗します。 spyオブジェクトは実際のオブジェクトのように動作するので、 whenコードでspyList.get(0)を呼び出すときにItemがなくExceptionが発生する可能性があります。

 java.lang.IndexOutOfBoundsException: Index: 0, Size: 0

 	at java.util.ArrayList.rangeCheck(ArrayList.java:659)
 	at java.util.ArrayList.get(ArrayList.java:435)

こういうときは、 doReturn().when()のパターンに変更してくれれば、正常にstubbingすることができます。

@Test
public void spyingList_when_ok() {
    List spyList = spy(ArrayList.class);

    doReturn("apple").when(spyList).get(0);
    doReturn("kiwi").when(spyList).get(1);
    doReturn(10).when(spyList).size();

    assertEquals("apple", spyList.get(0));
    assertEquals("kiwi", spyList.get(1));
    assertEquals(10, spyList.size());
}

したがって、spyオブジェクトをstubbingするとき when().thenReturn()パターンを使用せずに doReturn().when()パターンを使用することをお勧めします。

Exception発生

特定の状況でExceptionが発生することがあります。 when().thenThrow(ExceptionClass)パターンを使用します。

@Test
public void whenGetUserInfo_throwException() {
    Contacts contactsMock= mock(Contacts.class);
    when(contactsMock.getUserInfo(anyInt()))
            .thenThrow(NullPointerException.class);

    contactsMock.getUserInfo(0);
}

thenThrow()の引数としてクラス名を渡すことなく、 new NullPointerException(msg)のようにExceptionオブジェクトを生成して転送することもできます。

@Test
public void whenGetUserInfo_throwExceptionWithMsg() {
    Contacts contactsMock= mock(Contacts.class);
    when(contactsMock.getUserInfo(anyInt()))
            .thenThrow(new NullPointerException("Null pointer exception happened"));

    contactsMock.getUserInfo(0);
}

上記のコードを実行すると、Exceptionが発生し、テストは失敗します。 Exceptionが発生することを確認したので、テストはパスする必要があります。

次のように、NullPointerExceptionが発生しない場合、テストは失敗し、Exceptionが発生した場合、成功するように実装することができます。

@Test
public void whenGetUserInfo_throwException2() {
    Contacts contactsMock= mock(Contacts.class);
    when(contactsMock.getUserInfo(anyInt()))
            .thenThrow(NullPointerException.class);

    try {
        contactsMock.getUserInfo(0);
        fail();
    } catch (NullPointerException e) {
        // pass
    }
}

より簡単で、可読性が良い方法は、 @Testに次のように例外が発生すると明示することです。 もしExceptionが発生した場合、テストは成功しており、それ以外の場合、テストは失敗します。

@Test(expected = NullPointerException.class)
public void whenGetUserInfo_throwException3() {
    Contacts contactsMock= mock(Contacts.class);
    when(contactsMock.getUserInfo(anyInt()))
            .thenThrow(NullPointerException.class);

    contactsMock.getUserInfo(0);
}

Void-Return Typeメソッドで例外が発生

Voidに設定されたメソッドは、次のように doThrow().when()パターンでExceptionが発生するように実装する必要があります。

@Test(expected = NullPointerException.class)
public void whenCallsVoidReturnTypeMethod_throwException() {
    Contacts contactsMock= mock(Contacts.class);
    doThrow(NullPointerException.class)
            .when(contactsMock)
            .addUserInfo(anyString(), anyString());

    contactsMock.addUserInfo("AAA", "123-456");
}

spyオブジェクトで例外が発生

次のようにSpyオブジェクトもMockと同じように、Exceptionを発生させることができます。

@Test(expected = NullPointerException.class)
public void whenGetUserInfo_throwException4() {
    Contacts contactsMock= spy(Contacts.class);
    when(contactsMock.getUserInfo(anyInt()))
            .thenThrow(NullPointerException.class);

    contactsMock.getUserInfo(0);
}

まとめ

Mockitoライブラリを使用してMockとSpyオブジェクトを作成しverifyで検証する方法を説明しました。 また、whenにstubbingして、特定の値を返すか、例外を発生させるようにする方法を説明しました。

Related Posts

codechachaCopyright ©2019 codechacha