山崎屋の技術メモ

IT業界で働く中でテクノロジーを愛するSIerのシステムエンジニア👨‍💻 | AndroidとWebアプリの二刀流🧙‍♂️ | コードの裏にあるストーリーを綴るブログ執筆者✍️ | 日々進化するデジタル世界で学び続ける探究者🚀 | #TechLover #CodeArtisan、気になること、メモしておきたいことを書いていきます。

Mockito mock() と spy() の違いとサンプルコード

前回は mock() メソッドを使いメソッドの Mock 化を行いました。
www.shookuro.com

今回は mock() と spy() の違いをサンプルコードで確認したいと思います。

バージョン:
Java 11
junit 4.13
mockito 3.3.3

mock() と spy() の違い

mock() はインスタンスの非 static 且つ public のメソッドをすべて Mock 化します。

なので一部のメソッドを実装のまま使いたい場合には適しません。

spy() は明示的に指定したメソッドのみを Mock 化します。

Mock 化しないメソッドは実装通りのふるまいとなります。

サンプルコードで確認

package org.yyama;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;

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

import org.junit.Test;

public class TestSample {
	@Test
	public void test01() {
		List<String> mock = mock(List.class);
		List<String> spy = spy(new ArrayList<>());

		mock.add("a");
		mock.add("b");
		mock.add("c");
		
		spy.add("a");
		spy.add("b");
		spy.add("c");

		System.out.println(mock.size()); // → 0
		System.out.println(spy.size()); // → 3
	}
}

mock() で作ったインスタンスは add メソッドが Mock 化されているので(もちろん size メソッドも Mock 化されています。)、実際には何も処理されません。

spy() で作ったインスタンスは add メソッドが通常通り動作し、3 つの要素が追加されています。

spy で一部のメソッドを Mock 化する

package org.yyama;

import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;

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

import org.junit.Test;

public class TestSample {
	@Test
	public void test01() {
		List<String> spy = spy(new ArrayList<>());

		doReturn(1).when(spy).size();

		spy.add("a");
		spy.add("b");
		spy.add("c");

		System.out.println(spy.size()); // → 1
		
		System.out.println(spy.get(0)); // → a
		System.out.println(spy.get(1)); // → b
		System.out.println(spy.get(2)); // → c
	}
}

変数 spy の要素数は 3 にかかわらず、標準出力に 1 が出力されます。
「doReturn(1).when(spy).size();」で size メソッドを Mock 化し、1 を返すように設定しています。

「doReturn(1).when(spy).size();」の代わりに「when(spy.size()).thenReturn(1);」としても同じ結果が得られます。

次に戻り値のない List#add メソッドを Mock 化してみます。

package org.yyama;

import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.spy;

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

import org.junit.Test;

public class TestSample {
	@Test
	public void test01() {
		List<String> spy = spy(new ArrayList<>());

		doNothing().when(spy).add(0, "a");

		spy.add(0, "a");

		System.out.println(spy.size()); // → 0

		// System.out.println(spy.get(0)); // → IndexOutOfBoundsException
	}
}

戻り値のない List#add(int index, E element) を Mock 化し、何も処理をさせないように設定しています。

この場合、add メソッドで "a" をリストに追加しているにも関わらずサイズは 0 のまま、get(0) を呼び出すと例外が発生します。

まとめ

mock() と spy() の違いをサンプルコードで確認して、 spy() を使用したメソッドの Mock 化を行いました。
 
適材適所で使い分けられるようにしましょう。

本日はこれまで!

JUnit の基本について書いた記事もぜひご覧ください。
www.shookuro.com

実践 JUnit ―達人プログラマーのユニットテスト技法

実践 JUnit ―達人プログラマーのユニットテスト技法