mockitomockitoを使い始める


備考

MockitoはシンプルなAPIを使用して読みやすい単体テストを書く能力を提供することを目指すJava Mockingフレームワークです。他のフレームワークが使用するexpect-run-verifyパターンを残すことによって、他のモッキングフレームワークとは異なります。

代わりに、(非最終的な)クラスとインタフェースを模擬する1つの方法しか知らず、柔軟な引数マッチャーに基づいて検証とスタブを行うことができます。

現在のバージョン1.10.19は、

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>1.10.19</version>
</dependency>

またはグラデル

repositories { jcenter() }
dependencies { testCompile "org.mockito:mockito-core:1.+" }

バージョン2はまだベータ版です。

バージョン

バージョン Maven Central リリースノート発売日
2.1.0 モキトコア 変更 2016年10月4日
1.10.19 モキトコア 変更 2014-12-31

モックされたオブジェクトにビヘイビアを追加する

Mockito.when(mock.returnSomething()).thenReturn("my val");

mock.returnSomething(); // returns "my val"
mock.returnSomething(); // returns "my val" again
mock.returnSomething(); // returns "my val" again and again and again...
 

2番目の呼び出しで異なる値が必要な場合は、必要な戻り引数をthenReturnメソッドに追加できます。

Mockito.when(mock.returnSomething()).thenReturn("my val", "other val");

mock.returnSomething(); // returns "my val"
mock.returnSomething(); // returns "other val"
mock.returnSomething(); // returns "other val" again
 

モックに動作を追加せずにメソッドを呼び出すと、nullが返されます:

barMock.mock.returnSomethingElse(); // returns null
 

モックされたメソッドにパラメータがある場合は、値も宣言する必要があります。

Mockito.when(mock.returnSomething("param 1")).thenReturn("my val 1");
Mockito.when(mock.returnSomething("param 2")).thenReturn("my val 2");

mock.returnSomething("param 1"); // returns "my val 1"
mock.returnSomething("param 2"); // returns "my val 2"
mock.returnSomething("param 3"); // returns null
 

param値を気にしない場合、Matchers.any()を使用できます:

Mockito.when(mock.returnSomething(Matchers.any())).thenReturn("p1");

mock.returnSomething("param 1"); // returns "p1"
mock.returnSomething("param other"); // returns "p1"
 

例外をスローするには、次のメソッドを使用します。

Mockito.when(mock.returnSomething()).thenThrow(new Exception());

mock.returnSomething(); // throws Exception
 

モックに渡された引数をチェックする

このクラスがあると仮定し、 doSmth メソッドをテストしたいと思います。この場合、パラメータ "val"がfoo 渡されているかどうかを確認する必要があります。オブジェクトfoo は嘲笑されます。

public class Bar {

    private final Foo foo;

    public Bar(final Foo foo) {
        this.foo = foo;
    }

    public void doSmth() {
        foo.bla("val");
    }
}
 

ArgumentCaptor これを実現できます:

@Mock
private Foo fooMock;

@InjectMocks
private Bar underTest;

@Captor
private ArgumentCaptor<String> stringCaptor;

@Test
public void should_test_smth() {
    underTest.doSmth();

    Mockito.verify(fooMock).bla(stringCaptor.capture());

    assertThat(stringCaptor.getValue(), is("val"));
}
 

Mockitoでモックされたオブジェクトを作成する

Mockitoでオブジェクトをモックするには、2つの方法があります。

  • アノテーションを介して
  • モック関数を介して

アノテーションを使用:

JUnitテストランナー:

@RunWith(MockitoJUnitRunner.class)
public class FooTest {
    @Mock
    private Bar barMock;

    // ...
}
 

また、 MockitoJUnitRunner と同じ機能を提供するMockitoのJUnit @Rule 使うこともできますが、 @RunWith テストランナーは必要ありません。

public class FooTest {
    @Rule
    public MockitoRule mockito = MockitoJUnit.rule();        

    @Mock
    private Bar barMock;

    // ...
}
 

@RunWith@Rule アノテーションを使うことができない場合は、 "手札"を初期化することもできます:

public class FooTest {
    @Mock
    private Bar barMock;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
    }

    // ...
}
 

モック機能を介して:

public class FooTest {
    private Bar barMock = Mockito.mock(Bar.class);

    // ...
}
 

型の消去のため、上記のようにジェネリッククラスをモックすることはできません。基本クラスをモックし、明示的に適切なジェネリック型にキャストする必要があります:

public class FooTest {
    private Bar<String> genericBarMock = (Bar<String>) Mockito.mock(Bar.class);

    // ...
}
 

インストールとセットアップ

インストール

Mockitoをインストールする好ましい方法は、選択したビルドシステムでmockito-core 依存することを宣言することです。 2016年7月22日現在、最新の非ベータ版は1.10.19ですが、 2.xは既に移行されることが推奨されています

Maven

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>1.10.19</version>
    <scope>test</scope>
</dependency>
 

受け台

repositories { jcenter() }
dependencies { testCompile "org.mockito:mockito-core:1.+" }
 

Mockito自体に加えて、HamcrestとObjenesisを含むmockito-all もあります。主にAntユーザのためにMavenを介して配布されていますが、Mockito 2.xでは配布が中止されています。


インポート

Mockito施設のほとんどは、静的メソッドですorg.mockito.Mockito 。したがって、Mockitoはこのようにしてクラスに静的にインポートできます:

import static org.mockito.Mockito.*;
 

ドキュメントエントリポイントは、このクラスのjavadocにあります。

オブジェクトのいくつかのメソッドをモックする

オブジェクトのいくつかのメソッドは、mockitoのspy() を使って嘲笑されます。

たとえば、メソッドクラスが動作するにはいくつかのWebサービスが必要であるとします。

public class UserManager {

    List<User> users;        

    public UserManager() {
        user = new LinkedLisk<User>();
    }
    
    public void addUser(User user) {
        if (isValid(user)) {
            user.add(user);
        } else {
            throw new NotValidUserException();
        }
    }
    
    protected boolean isValid(User user) {
        //some online web service to check if user is valid
    }
    
    public int numberOfUsers() {
        return users.size();
    }
}
 

addUser メソッドは、 UserManager 便利なテストを行うためにテストする必要があります。しかし、ここでは依存関係が見つかり、 isValid はコードに含まれていない外部Webサービスが必要です。次に、この外部依存関係を中和する必要があります。

この場合、 isValid をモックするだけで、残りのUserManager メソッドをテストすることができます。

@Test
public void testAddUser() {
    User user = mock(User.class);
    UserManager  manager = spy(new UserManager());
    
    //it forces to manager.isValid to return true
    doReturn(true).when(manager).isValid(anyObject());
    
    manager.addUser(user);
    assertTrue(manager.numberOfUsers(), 1);
} 
 

user が有効でないシナリオを簡単に確認できます。

@Test(expectedExceptions = NotValidUserException.class)
public void testNotValidAddUser() {
    User user = mock(User.class);
    UserManager  manager = spy(new UserManager());
    
    //it forces to manager.isValid to return false
    doReturn(false).when(manager).isValid(anyObject());
    
    manager.addUser(user);
} 
 

シンプルな最小のモッキート試験

この例は、嘲笑されたArrayList を使った最小のMockitoテストを示しています:

import static org.mockito.Mockito.*;
import static org.junit.Assert.*;

import java.util.ArrayList;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class MockitoTest
{
    @Mock
    ArrayList<String> listMock;

    @Test
    public void testAppend() {
        // configure the mock to return "foobar" whenever "get()" 
        // is called on "listMock" with an int value as parameter
        doReturn("foobar").when(listMock).get(anyInt());            
        String result = listMock.get(0);
        
        assertEquals("foobar", result);
    }
}
 

Mockitoを使った簡単な単体テスト

テストするクラスは次のとおりです。

public class Service {

    private Collaborator collaborator;

    public Service(Collaborator collaborator) {
        this.collaborator = collaborator;
    }
    
    public String performService(String input) {
        return collaborator.transformString(input);
    }
}
 

その協力者は:

public class Collaborator {

    public String transformString(String input) {
        return doStuff();
    }

    private String doStuff() {
        // This method may be full of bugs
        . . .
        return someString;
    }

}
 

私たちのテストでは、 Collaborator とそのバグの依存関係を解消したいので、 Collaborator を模倣しようCollaborator ます:

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

import org.junit.Test;

public class ServiceTest {
    @Test
    public void testPerformService() throws Exception {
        // Configure mock
        Collaborator collaboratorMock = mock(Collaborator.class);
        doReturn("output").when(collaboratorMock).transformString("input");

        // Perform the test
        Service service = new Service(collaboratorMock);
        String actual = service.performService("input");
        
        // Junit asserts
        String expected = "output";
        assertEquals(expected, actual);
    }  
}
 

スタッビングボイドメソッド

void メソッドは、 doThrow()doAnswer()doNothing()doCallRealMethod()の一連のメソッドを使用してスタブすることができます。

Runnable mock = mock(Runnable.class);

doThrow(new UnsupportedOperationException()).when(mock).run();

mock.run(); // throws the UnsupportedOperationException
 

ことに注意してくださいvoid の方法が使用してスタブすることはできませんwhen(..) 、コンパイラは好きではない原因void 引数としてメソッドを。

Mockitoアノテーションの使用

テストするクラスは次のとおりです。

public class Service{

    private Collaborator collaborator;

    public Service(Collaborator collaborator){
        this.collaborator = collaborator;
    }
    
    
    public String performService(String input){
        return collaborator.transformString(input);
    }
}
 

その協力者は:

public class Collaborator {

    public String transformString(String input){
        return doStuff();
    }

    private String doStuff()
    {
        // This method may be full of bugs
        . . .
        return someString;
    }

}
 

私たちのテストでは、 Collaborator とそのバグの依存関係を解除したいので、 Collaborator を模倣しようCollaborator ます。 @Mock アノテーションを使用すると、テストごとにさまざまなモックのインスタンスを作成する便利な方法です。

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.InjectMocks;
import org.mockito.runners.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class ServiceTest {

    @Mock
    private Collaborator collaboratorMock;

    @InjectMocks
    private Service service;
    
    @Test
    public void testPerformService() throws Exception {
        // Configure mock
        doReturn("output").when(collaboratorMock).transformString("input");            

        // Perform the test
        String actual = service.performService("input");
        
        // Junit asserts
        String expected = "output";
        assertEquals(expected, actual);
    }
    
    
    @Test(expected=Exception.class)
    public void testPerformServiceShouldFail() throws Exception {
        // Configure mock
        doThrow(new Exception()).when(collaboratorMock).transformString("input");

        // Perform the test
        service.performService("input");
    }
}
 

Mockitoは依存性注入を次の順序で解決しようと試みます:

  1. コンストラクタベースの注入モックは、ほとんどの引数を使ってコンストラクタに注入されます(引数が見つからない場合はnullが渡されます)。オブジェクトがコンストラクタを介して正常に作成された場合、他の戦略は適用されません。
  2. セッターベースのインジェクションモックはタイプ別に注入されます。同じタイプのプロパティが複数ある場合は、プロパティ名とモック名が一致します。
  3. 直接フィールド注入 - セッターベース注入と同じ。

前述の戦略のいずれかが失敗した場合、失敗は報告されないことに注意してください。

最新バージョンのMockitoでは、このメカニズムの詳細については、最新の@InjectMocks を参照してください。

モックされたオブジェクトのメソッド呼び出しを確認する

モックされたオブジェクトでメソッドが呼び出されたかどうかを確認するには、 Mockito.verify メソッドを使用できます。

Mockito.verify(someMock).bla();
 

この例では、メソッドblasomeMock モックオブジェクトで呼び出されたと主張します。

特定のパラメータでメソッドが呼び出されたかどうかを確認することもできます。

Mockito.verify(someMock).bla("param 1");
 

あなたはメソッドが呼び出されなかったことを確認したい場合は、追加渡すことができVerificationMode ためのパラメータverify

Mockito.verify(someMock, Mockito.times(0)).bla();
 

これは、このメソッドが複数回呼び出されたことを確認したい場合にも機能します(この場合、メソッドbla が23回呼び出されたことを確認します)。

Mockito.verify(someMock, Mockito.times(23)).bla();
 

これらはVerificationMode パラメータのより多くの例であり、メソッドの呼び出し回数をより詳細に制御できます。

Mockito.verify(someMock, Mockito.never()).bla(); // same as Mockito.times(0)

Mockito.verify(someMock, Mockito.atLeast(3)).bla(); // min 3 calls

Mockito.verify(someMock, Mockito.atLeastOnce()).bla(); // same as Mockito.atLeast(1)

Mockito.verify(someMock, Mockito.atMost(3)).bla(); // max 3 calls
 

ArgumentCaptorによる引数の検証

モックで呼び出されたメソッドの引数を検証するには、 ArgumentCaptor クラスを使用します。これにより、引数をあなたのテストメソッドに抽出し、そのメソッドに対してアサーションを実行することができます。

この例では、指定されたIDを持つユーザーの名前を更新するメソッドをテストします。このメソッドはユーザーを読み込み、 name 属性を指定された値で更新し、後で保存します。このテストでは、 save メソッドに渡された引数が、正しいIDと名前を持つUser オブジェクトであることを確認する必要があります。

// This is mocked in the test
interface UserDao {
    void save(User user);
}

@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {
    @Mock
    UserDao userDao;

    @Test
    public void testSetNameForUser() {
        UserService serviceUnderTest = new UserService(userDao);
        
        serviceUnderTest.setNameForUser(1L, "John");

        ArgumentCaptor<User> userArgumentCaptor = ArgumentCaptor.forClass(User.class);
        
        verify(userDao).save(userArgumentCaptor.capture());
        User savedUser = userArgumentCaptor.getValue();
        assertTrue(savedUser.getId() == 1);
        assertTrue(savedUser.getName().equals("John"));
    }
}
 

ArgumentMatcherによる引数の検証

MockitoはArgumentMatcher<T> を検証するための抽象ArgumentMatcher<T> クラスとともにMatcher<T> インタフェースを提供します。これは、 ArgumentCaptor と同じユースケースに対して異なるアプローチを使用します。さらに、ArgumentMatcherはモックにも使用できます。どちらのユースケースも、合理的に読みやすいテストコードを提供するMockito.argThat() メソッドを使用します。

verify(someMock).someMethod(Mockito.argThat(new ArgumentMatcher<String>() {
       
    @Override
    public boolean matches(Object o) {
        return o instanceof String && !((String)o).isEmpty();
    }

});        
 

ArgumentMatcherのJavaDocsから:

警告:複雑な引数マッチング、特にカスタム引数マッチャーを使用することで合理的であること。テストが読みにくくなる可能性があるためです。時々、モックに渡される引数にequals()を実装するほうが良いでしょう(Mockitoは引数マッチングのために自然にequals()を使います)。これは、テストクリーナーを作ることができます。