LoginSignup
6
6

More than 5 years have passed since last update.

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

Posted at

システム日付けを固定したい

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

前提 テスト対象のクラスは??

今回は下記をテストするJUnitを記述することにする。

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!

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

@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でミリ秒まで出したい、、

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


@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ライブラリを使用して下記の方法を思いついた。

@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();
        }
    }

これで、日付けも固定できてカバレッジも取得できてよかったよかった!

6
6
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
6