読者です 読者をやめる 読者になる 読者になる

プログラマとプロマネのあいだ

プログラマもやるし、プロマネもやるし、たまに似非アーキとか営業っぽいこともやる

テスト対象クラス内でnewで生成されている依存オブジェクトをモックする

Java JMockit

テスト対象クラス(DateUtil.java

import java.text.SimpleDateFormat;
import java.util.Date;

public class DateUtil {
	public static String getCurrentDate() {
		Date d = new Date();
		return new SimpleDateFormat("yyyy/MM/dd").format(d);
	}
}

テストクラス(DateUtilTest.java

import static org.hamcrest.core.Is.*;
import static org.junit.Assert.*;

import java.util.Calendar;
import java.util.Date;

import mockit.Mock;
import mockit.MockUp;
import mockit.integration.junit4.JMockit;

import org.junit.Test;
import org.junit.runner.RunWith;

// new Objectをモックするテスト
@RunWith(JMockit.class)
public class DateUtilTest {	
	@Test
	public void testGetCurrentDate() {
		// 期待値は2015/05/05
		Calendar c = Calendar.getInstance();
		c.set(2015, 4, 5);
		Date expected = c.getTime();

		// 記録フェーズ
		new MockUp<Date>() {
			@Mock
			public void $init() {
				this.getMockInstance().setTime(expected.getTime());
			}
		};
		
		// リプレイフェーズ
		String strDate = DateUtil.getCurrentDate();
		
		// 検証フェーズ
		assertThat(strDate, is("2015/05/05"));
	}
}

コンストラクタのモックはExpectations APIだとできないみたいで、Mockups APIを使う必要があります。


これが出来るのがJMockitの強みかと思っていたのですが、JMockitのページ読んでたら、PowerMock(Mockito)でも出来るみたいです。

JMockit - Mocking Toolkit Comparison Matrix

PowerMock(Mockito)版テストクラス(DateUtilTest.java

import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import static org.powermock.api.mockito.PowerMockito.*;

import java.util.Calendar;
import java.util.Date;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

// new Objectをモックするテスト
@RunWith(PowerMockRunner.class)
@PrepareForTest(DateUtil.class)
public class DateUtilTest {	
	@Test
	public void testGetCurrentDate() throws Exception {
		// 期待値は2015/05/05
		Calendar c = Calendar.getInstance();
		c.set(2015, 4, 5);
		Date expected = c.getTime();

		// 記録フェーズ
		Date dateMocked = mock(Date.class);
		whenNew(Date.class).withNoArguments().thenReturn(dateMocked);
		when(dateMocked.getTime()).thenReturn(expected.getTime());
		
		// リプレイフェーズ
		String strDate = DateUtil.getCurrentDate();
		
		// 検証フェーズ
		assertThat(strDate, is("2015/05/05"));
	}
}

どっちが良いのかは好みもあるのでしょうが、個人的には、テスト対象クラス(DateUtil.java)をクラスレベルで宣言(@PrepareForTest(DateUtil.class))しなければならない、PowerMock(Mockito)はイマイチかなあと思います。
あとは、依存jarが一つで済むJMockitの方が身軽で良いかなあと。