我有一个使用当前时间做一些计算功能。 我想使用到的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());
}
是否有可能来嘲笑吗? 我不希望改变“测试”的代码,以便进行测试。
做正确的事情,就是要调整你的代码,使其更容易测试,如下图所示。 重组你的代码,以消除日期的直接依赖关系可以让你注入正常运行时间和测试运行时不同的实现方式:
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
}
}
你可以通过做这个PowerMock ,这增强了对的Mockito能够嘲笑静态方法。 然后,你可以嘲笑System.currentTimeMillis()
这是在new Date()
最终会从时间。
你可以 。 我不打算推进对你是否应该发表意见。
一种方法,不直接回答这个问题,但可能解决不了根本问题(有重复性测试),是允许的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;
}
});
}