JUnitでシステム日付けを固定する方法

## システム日付けを固定したい
仕事で単体試験フェーズになった時、日付けを固定したいという場面はよくでてくる。
今回はMockライブラリを用いて時間を固定してみた。

## 前提　テスト対象のクラスは？？
今回は下記をテストするJUnitを記述することにする。

```java
public class OutputDate {
    public void getDate(){
    	SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 hh時mm分ss秒SSSミリ秒");
    	System.out.println(sdf.format(new Date()));
    }
}
```
上手くいけばnew Date()で出力される日時が固定されるはず！


##まずはPowerMock!
上司に教えてもらい下記のような方法で実現できることを知った。


```java
@RunWith(PowerMockRunner.class)
@PrepareForTest({OutputDate.class, Date.class})
public class FixSysDateUsingPowerMock1 {

	OutputDate output = new OutputDate();

	@Before
	public void setUp(){
		setDatemock();
	}


	@Test
	public void test() {
		output.getDate();
	}

	/**
	 * システム日付けを固定する
	 * この方法だとミリ秒まで固定できない
	 */
	private static void setDatemock(){
		Calendar cal = Calendar.getInstance();
		//時間を2018年1月1日10時10分10秒にセットする(月は0が1月)
		cal.set(2018, 0,1,10,10,10);
		try {
			PowerMockito.whenNew(Date.class).withNoArguments().thenReturn(cal.getTime());
			when(new Date()).thenReturn(cal.getTime());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
```

setDatemock()メソッドでCalendarクラスのset()メソッドを用いて固定化を試みている方法である。最初はこれでいいやと思ったのだが、**これだとミリ秒を固定できない。**setメソッドでミリ秒まで指定するものが無いから(自分が探した範囲では)である。


## PowerMockでミリ秒まで出したい、、
どうしようか悩んでいたところ、文字列でうまくできないかな？という上司からのヒントのもと、以下の方法を思いついた。


```java

@RunWith(PowerMockRunner.class)
@PrepareForTest({ OutputDate.class, Date.class })
public class FixSysDateUsingPowerMock2 {

	OutputDate output = new OutputDate();

	@Before
	public void setUp() {
		setDatemock();
	}

	@Test
	public void test() {
		output.getDate();
	}

	/**
	 * システム日付けを固定する
	 * この方法だとミリ秒まで固定する
	 */
	private static void setDatemock() {
		String strDate = "2018-01-01 10:10:10.111";
		// 時間を2018年1月1日10時10分10秒にセットする(月は0が1月)
		SimpleDateFormat sdFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS");
		try {
			PowerMockito.whenNew(Date.class).withNoArguments().thenReturn(sdFormat.parse(strDate));
			when(new Date()).thenReturn(sdFormat.parse(strDate));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
```
固定したい日時を、文字列からDate形に無理やり変換する方法である。
この方法で、ミリ秒も固定することができた。
めでたしめでたし、、、

## JMockitで日時固定できないかな？
上記の方法ですっかり安心していたのだが、カバレッジ取得の際に少し困る問題が発生した。
Eclipseでカバレッジを取得する時に使用するツール「Eclemma」がテストランナーでPowerMockRunner.classを指定すると上手く動作しないようなのである。
既知の問題らしく、一応以下の方法のように@Ruleを使用する方法で解決するらしい。

PowerMockを使ってEclemmaでカバレッジも取得する方法はこちら→https://code.i-harness.com/ja/q/1647e8c

しかし、単体試験時の自分はせっかくだし別の方法ないかな～と模索してた。
そしてJMockitライブラリを使用して下記の方法を思いついた。

```java
@RunWith(JMockit.class)
public class FixSysDateUsingJMockit {

	//private staticをつけないとダメ
	@Mocked
	private static OutputDate output = new OutputDate();

	@Before
	public void setUp() throws Exception{
		setDatemock();
	}

	@Test
	public void test() {
		output.getDate();
	}

	private  void setDatemock() throws Exception {
		String strDate = "2018-01-01 10:10:10.111";
		SimpleDateFormat sdFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS");
		new CurrentTimeMock(sdFormat.parse(strDate));
	}

	/**
	 * dateモック用クラス
	 */
	public static class CurrentTimeMock extends MockUp<System>{
		Date mockTime;

		public CurrentTimeMock(Date mockTime){
			this.mockTime = mockTime;
		}

		@Mock
		public long currentTimeMillis(){
			return mockTime.getTime();
		}
	}
```
これで、日付けも固定できてカバレッジも取得できてよかったよかった！