web-dev-qa-db-ja.com

Mockitoを使用してJavaで新しいDate()をモックする方法

現在の時間を使用して計算を行う関数があります。 mockitoを使用してモックしたいと思います。

テストしたいクラスの例:

public class ClassToTest {
    public long getDoubleTime(){
        return new Date().getTime()*2;
    }
}

次のようなものが欲しい:

@Test
public void testDoubleTime(){
   mockDateSomeHow(Date.class).when(getTime()).return(30);
   assertEquals(60,new ClassToTest().getDoubleTime());
}

それをm笑することは可能ですか?テストするために「テスト済み」コードを変更したくありません。

36
Jordi P.S.

正しいことは、以下に示すようにコードを再構築して、テストしやすくすることです。 Dateに直接依存しないようにコードを再構築すると、通常のランタイムとテストランタイムに異なる実装を挿入できます。

interface DateTime {
    Date getDate();
}

class DateTimeImpl implements DateTime {
    @Override
    public Date getDate() {
       return new Date();
    }
}

class MyClass {

    private final DateTime dateTime;
    // inject your Mock DateTime when testing other wise inject DateTimeImpl

    public MyClass(final DateTime dateTime) {
        this.dateTime = dateTime;
    }

    public long getDoubleTime(){
        return dateTime.getDate().getTime()*2;
    }
}

public class MyClassTest {
    private MyClass myClassTest;

    @Before
    public void setUp() {
        final Date date = Mockito.mock(Date.class);
        Mockito.when(date.getTime()).thenReturn(30L);

        final DateTime dt = Mockito.mock(DateTime.class);
        Mockito.when(dt.getDate()).thenReturn(date);

        myClassTest = new MyClass(dt);
    }

    @Test
    public void someTest() {
        final long doubleTime = myClassTest.getDoubleTime();
        assertEquals(60, doubleTime);
    }
}
51
munyengm

リファクタリングできないレガシーコードがあり、System.currentTimeMillis()に影響を与えたくない場合は、PowermockおよびPowerMockitoを使用してこれを試してください。

//note the static import
import static org.powermock.api.mockito.PowerMockito.whenNew;

@PrepareForTest({ LegacyClassA.class, LegacyClassB.class })

@Before
public void setUp() throws Exception {

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    sdf.setTimeZone(TimeZone.getTimeZone("PST"));

    Date NOW = sdf.parse("2015-05-23 00:00:00");

    // everytime we call new Date() inside a method of any class
    // declared in @PrepareForTest we will get the NOW instance 
    whenNew(Date.class).withNoArguments().thenReturn(NOW);

}

public class LegacyClassA {
  public Date getSomeDate() {
     return new Date(); //returns NOW
  }
}
20
Daniel Mora

あなたはcouldPowerMock を使用してこれを行います。これにより、Mockitoが静的メソッドをモックできるようになります。その後、 mock System.currentTimeMillis() になります。これは、new Date()が最終的に時間を取得する場所です。

あなたcouldshouldであるかどうかについて意見を述べるつもりはありません。

6
Tom Anderson

質問に直接答えることはできませんが、根本的な問題(再現可能なテストがある)を解決するかもしれないアプローチの1つは、テストのパラメーターとしてDateを許可し、デフォルト日付にデリゲートを追加することです。

そのようです

_public class ClassToTest {

    public long getDoubleTime() {
      return getDoubleTime(new Date());
    }

    long getDoubleTime(Date date) {  // package visibility for tests
      return date.getTime() * 2;
    }
}
_

実動コードでは、getDoubleTime()を使用し、getDoubleTime(Date date)に対してテストします。

1
DerMike
Date now = new Date();    
now.set(2018, Calendar.FEBRUARY, 15, 1, 0); // set date to 2018-02-15
//set current time to 2018-02-15
mockCurrentTime(now.getTimeInMillis());

private void mockCurrentTime(long currTimeUTC) throws Exception {
    // mock new dates with current time
    PowerMockito.mockStatic(Date.class);
    PowerMockito.whenNew(Date.class).withNoArguments().thenAnswer(new Answer<Date>() {

        @Override
        public Date answer(InvocationOnMock invocation) throws Throwable {
            return new Date(currTimeUTC);
        }
    });

    //do not mock creation of specific dates
    PowerMockito.whenNew(Date.class).withArguments(anyLong()).thenAnswer(new Answer<Date>() {

        @Override
        public Date answer(InvocationOnMock invocation) throws Throwable {
            return new Date((long) invocation.getArguments()[0]);
        }
    });

    // mock new calendars created with time zone
    PowerMockito.mockStatic(Calendar.class);
    Mockito.when(Calendar.getInstance(any(TimeZone.class))).thenAnswer(new Answer<Calendar>() {
        @Override
        public Calendar answer(InvocationOnMock invocation) throws Throwable {
            TimeZone tz = invocation.getArgumentAt(0, TimeZone.class);
            Calendar cal = Calendar.getInstance(tz);
            cal.setTimeInMillis(currTimeUTC);
            return cal;
        }
    });
}
0
Avraham Shalev