現在の時間を使用して計算を行う関数があります。 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笑することは可能ですか?テストするために「テスト済み」コードを変更したくありません。
正しいことは、以下に示すようにコードを再構築して、テストしやすくすることです。 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);
}
}
リファクタリングできないレガシーコードがあり、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
}
}
あなたはcouldPowerMock を使用してこれを行います。これにより、Mockitoが静的メソッドをモックできるようになります。その後、 mock System.currentTimeMillis()
になります。これは、new Date()
が最終的に時間を取得する場所です。
あなたcould。 shouldであるかどうかについて意見を述べるつもりはありません。
質問に直接答えることはできませんが、根本的な問題(再現可能なテストがある)を解決するかもしれないアプローチの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)
に対してテストします。
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;
}
});
}