如何使用Mockito在java中模拟新的Date()

Jor*_*.S. 37 java junit unit-testing mocking mockito

我有一个使用当前时间进行一些计算的函数.我想用mockito嘲笑它.

我要测试的类的一个例子:

public class ClassToTest {
    public long getDoubleTime(){
        return new Date().getTime()*2;
    }
}
Run Code Online (Sandbox Code Playgroud)

我喜欢这样的东西:

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

有可能嘲笑吗?我不想更改"经过测试"的代码以便进行测试.

mun*_*ngm 51

正确的做法是重新构建代码,使其更易于测试,如下所示.重构代码以删除对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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这是解决这个问题的经典方法(你所谓的'DateTime`可能更具描述性地被称为`Clock`或类似的东西).但是,这确实意味着重构代码并添加一些复杂性纯粹是为了允许测试,这有点代码味道. (19认同)
  • 所以我做了,我认为这是一个很好的方法,但问题是如何使用 Mockito :D (3认同)
  • 可悲的是答案没有回答问题。如果我的代码代码使用的库使用了一个使用 `new Date()` 的库,并且我的测试停止工作,因为测试向量包含一个有效期直到昨天的证书,我不知道如何删除 `new Date() 的使用)` 以快速修复我的测试。 (3认同)

Dan*_*ora 21

如果你有遗留的代码,你不能重构,你不想影响System.currentTimeMillis(),请尝试使用PowermockPowerMockito

//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
  }
}
Run Code Online (Sandbox Code Playgroud)


Tom*_*son 6

可以通过使用PowerMock 实现这一点,它可以增强Mockito能够模拟静态方法.然后你可以模拟System.currentTimeMillis(),这是new Date()最终得到时间的地方.

可以.我不会就你是否应该提出意见.

  • 盲目地将每个"新"转换为包装器对象并通过注入的依赖项引入间接性只会使代码不一定冗长和困难.如果你在类中创建一个ArrayList或HashMap,你现在会创建一个ArrayListFactory或一个HashMapFactory并将其注入你的类吗?盲目地使用PowerMock在任何地方使用"new"都可以创建一个非常紧密耦合的系统.能够分辨出PowerMock等工具是否合适是成为熟练开发人员的一部分 (5认同)
  • 显然你不能再模拟 System 类了(我正在使用 Mockito 3.12)。它说:“不可能模拟 java.lang.System 的静态方法来避免干扰类加载,从而导致无限循环”。 (2认同)