論よりコードということでサンプルをつらつらと。
package study.mockito.helloworld; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import org.junit.Assert; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.Spy; import org.mockito.exceptions.verification.SmartNullPointerException; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; public class MockitoHelloworld { /* * 基本的な使い方 */ @Test public void testVerify() { // Mockito.mock() で Mock を作る List mockList = Mockito.mock(List.class); mockList.add("foo"); mockList.clear(); // Mockito.verify() で Mock のメソッドが呼び出されたことを確認する Mockito.verify(mockList).add("foo"); Mockito.verify(mockList).clear(); } /* * Mock に特定の振る舞いをさせる */ @Test public void testStub() { LinkedList mockList = Mockito.mock(LinkedList.class); // mockList.get(0) で "first" を返すように設定する Mockito.when(mockList.get(0)).thenReturn("first"); // 上記の通りに動作することを確認する Assert.assertEquals(mockList.get(0), "first"); // 設定していない引数には null が返る Assert.assertEquals(mockList.get(1), null); } /* * 特定の値ではなく型を使って振る舞いを設定する */ @Test(expected = IllegalArgumentException.class) public void testArgType() { Map mockMap = Mockito.mock(Map.class); // 引数が String 型のときは "String" を返す Mockito.when(mockMap.get(Mockito.anyString())).thenReturn("String"); // 引数が int 型のときは例外をスローする Mockito.when(mockMap.get(Mockito.anyInt())).thenThrow(IllegalArgumentException.class); // 文字列を渡したとき上記の通り動作することを確認する Assert.assertEquals(mockMap.get("foo"), "String"); // 数値を渡したときは IllegalArgumentException がスローされる mockMap.get(0); } /* * Mock が特定の引数で呼び出された回数を検証する */ @Test public void testVerifyNum() { List mockList = Mockito.mock(List.class); // foo: 1 回 mockList.add("foo"); // bar: 1 回 mockList.add("bar"); // baz: 2 回 mockList.add("baz"); mockList.add("baz"); // foo は 1 回呼び出された Mockito.verify(mockList, Mockito.times(1)).add("foo"); // bar も 1 回呼び出された Mockito.verify(mockList, Mockito.times(1)).add("bar"); // baz は 2 回呼び出された Mockito.verify(mockList, Mockito.times(2)).add("baz"); // hoge は 0 回呼び出された (= 呼び出されていない) Mockito.verify(mockList, Mockito.times(0)).add("hoge"); // Mockito.never() は Mockito.times(0) のシンタックスシュガー Mockito.verify(mockList, Mockito.never()).add("fuga"); } /* * 返り値が void 型のときの書き方 */ @Test(expected = IllegalArgumentException.class) public void testStubVoidMethod() { List mockList = Mockito.mock(List.class); // 返り値が void のときは when() の代わりに doXXX() ではじまる Mockito.doThrow(IllegalArgumentException.class).when(mockList).clear(); mockList.clear(); } /* * 呼び出し順序を検証する */ @Test public void testInOrderSingle() { List singleMock = Mockito.mock(List.class); // "first" -> "second" の順番で呼び出す singleMock.add("first"); singleMock.add("second"); // 順序を検証するには InOrder を使う InOrder inOrder = Mockito.inOrder(singleMock); // InOrder#verify() を順番に使う inOrder.verify(singleMock).add("first"); inOrder.verify(singleMock).add("second"); } /* * 複数のモックにまたがって呼び出し順序を検証する */ @Test public void testInOrderMultiple() { List firstMock = Mockito.mock(List.class); firstMock.add("first"); List secondMock = Mockito.mock(List.class); secondMock.add("second"); // Mockito.inOrder() に順序を検証したいモックをすべて渡す InOrder inOrder = Mockito.inOrder(firstMock, secondMock); inOrder.verify(firstMock).add("first"); inOrder.verify(secondMock).add("second"); } /* * Mock が呼び出されていないことを検証する */ @Test public void testInteractionsNeverHappend() { List firstMock = Mockito.mock(List.class); firstMock.add("first"); List secondMock = Mockito.mock(List.class); /* secondMock.add("second"); // このコメントアウト (外側) を外すとテストは fail する */ Mockito.verify(firstMock).add("first"); // firstMock の呼び出しによって secondMock に影響が及んでいないことを確認できる Mockito.verifyZeroInteractions(secondMock); } /* * 実際のオブジェクトを stub したり verify できる Spy にして使う */ @Test public void testRealObjectSpy() { List realObj = new LinkedList(); // 実際のオブジェクトから Spy をつくる List spyObj = Mockito.spy(realObj); // size() メソッドを使うときだけ実際にはありえない値を返すように設定する Mockito.when(spyObj.size()).thenReturn(100); // Spy に中身を 2 つ入れる spyObj.add("first"); spyObj.add("second"); // 2 つしか入れていないのに size() の返り値は 100 になる Assert.assertEquals(spyObj.size(), 100); // 呼び出しの検証もできる Mockito.verify(spyObj).add("first"); Mockito.verify(spyObj).add("second"); Mockito.verify(spyObj).size(); } private static class AnnotationSample { // @Mock アノテーションで Mock を Inject できる @Mock private List mock; // @Spy アノテーションで Spy を Inject できる @Spy private List spy = new ArrayList(); public List getMock() { return this.mock; } public List getSpy() { return this.spy; } } /* * Mock アノテーションの使い方 */ @Test public void testMockAnnotation() { AnnotationSample mockable = new AnnotationSample(); // Inject 前はふつうのオブジェクト Assert.assertEquals(mockable.getMock(), null); // Mock/Spy を Inject する MockitoAnnotations.initMocks(mockable); // 中身が Mock になった Mockito.when(mockable.getMock().get(0)).thenReturn("Mocked"); Assert.assertEquals(mockable.getMock().get(0), "Mocked"); Mockito.verify(mockable.getMock()).get(0); } /* * Spy アノテーションの使い方 */ @Test public void testSpyAnnotation() { AnnotationSample mockable = new AnnotationSample(); // Mock/Spy を Inject する MockitoAnnotations.initMocks(mockable); // 中身が Spy になった mockable.getSpy().add("hoge"); Mockito.verify(mockable.getSpy()).add("hoge"); } /* * Mock の呼び出し毎に返り値を変更する */ @Test(expected = NoSuchElementException.class) public void testConsecutiveCall() { Iterator mockIterator = Mockito.mock(Iterator.class); // Iterator らしく呼び出し毎に返り値を変化させていく Mockito.when(mockIterator.next()) .thenReturn("first") .thenReturn("second") .thenReturn("third") .thenThrow(NoSuchElementException.class); // 意図通りに変化するか確認する Assert.assertEquals(mockIterator.next(), "first"); Assert.assertEquals(mockIterator.next(), "second"); Assert.assertEquals(mockIterator.next(), "third"); mockIterator.next(); } /* * Mock の返り値をコールバック形式で設定する */ @Test public void testCallbackAnswer() { List mockList = Mockito.mock(List.class); // Answer インターフェースを実装すればいい Mockito.when(mockList.get(Mockito.anyInt())).thenAnswer(new Answer() { public Object answer(InvocationOnMock invocation) throws Throwable { Object[] args = invocation.getArguments(); return "index: " + args[0]; } }); Assert.assertEquals(mockList.get(100), "index: 100"); } private static class Parent { public Child getChild() { return new Child(); } } private static class Child { public String getMessage() { return "Hello, World!!"; } } /* * Mock が返す値の動作を変更する */ @Test public void testReturnType() { // 実際のメソッドを呼ぶ Parent mock = Mockito.mock(Parent.class, Mockito.CALLS_REAL_METHODS); Assert.assertEquals(mock.getChild().getMessage(), "Hello, World!!"); // 再起的にスタブを返す mock = Mockito.mock(Parent.class, Mockito.RETURNS_DEEP_STUBS); Assert.assertNull(mock.getChild().getMessage()); // 空のモックを返す mock = Mockito.mock(Parent.class, Mockito.RETURNS_MOCKS); Assert.assertEquals(mock.getChild().getMessage(), ""); // null をたたいても NPE の代わりにトレーサビリティに優れた SmartNPE をスローする mock = Mockito.mock(Parent.class, Mockito.RETURNS_SMART_NULLS); try { mock.getChild().getMessage(); } catch(SmartNullPointerException ex) { ; } // null を返す mock = Mockito.mock(Parent.class, Mockito.RETURNS_DEFAULTS); try { mock.getChild().getMessage(); } catch(NullPointerException ex) { ; } } private static class NotImplementsEqualsClass { private String name; public NotImplementsEqualsClass(String name) { this.name = name; } public String getName() { return name; } } /* * メソッド呼び出しの引数の検証に verify を使わず自分でやる */ @Test public void testCapturingArguments() { List mock = Mockito.mock(List.class); mock.add(new NotImplementsEqualsClass("foo")); /* * Object#equals() が実装されていないと実質的に等価でも比較できない * Mockito.verify(mock).add(new NotImplementsEqualsClass("foo")); */ // 代わりに ArgumentCaptor を使う ArgumentCaptorプロジェクトは Maven で作ったから pom.xml も。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>study.mockito.helloworld</groupId> <artifactId>study-mockito-helloworold</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <name>study-mockito-helloworold</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-all</artifactId> <version>1.9.5</version> </dependency> </dependencies> </project>Mockito めっちゃ使いやすい。