在方法中多次模拟 Calendar.getInstance 静态方法

Mocking Calendar.getInstance static method multiple times in a method

我目前正在使用 JMockito/PowerMock 以便我可以模拟静态方法的 return 值,在本例中为 Calendar.getInstance()。 我的问题与此非常相似,但不完全是: PowerMocking static does not return expected object

我还检查了其他各种类似的主题,但 none 其中的主题完全符合我的上下文。目前,我有这样的代码:

Calendar cal = Calendar.getInstance();
//modifies this instance
.....
.....
//then there is another call:
cal2 = Calendar.getInstance();
....
....

逻辑做什么在这里并不重要。当我尝试测试这个方法块时,我有这样的期望:

Calendar myOwn = //whatever
when(Calendar.getInstance()).thenReturn(myOwn);

所以在测试期间发生的测试是,在 Calendar.getInstance() 的第一次调用中,它成功地获得了 myOwn 对象,这正是我所期望的。显然,该方法随后对该对象进行了一些修改。

但是,在调试时,当下一次调用Calendar.getInstance()稍后发生时,变量cal2似乎得到了[=16=的修改版本] 之前的对象。我原以为它会得到 original myOwn 日历对象,但它没有。

我也试过重建对这个的期望(因为总共有两个 getInstance() 调用,至少在这个方法中):

when(Calendar.getInstance()).thenReturn(myOwn).theReturn(myOwn);

但运气不好。然后我想这是否是一个变量通过引用传递的问题,因为它是静态的但是,以下输出是有效的并且完全合乎逻辑,因为我单独测试了它。

Calendar cal = Calendar.getInstance();
System.out.println(cal.getTime()); //prints current date object (as expected)

cal.set(Calendar.YEAR, 2017);
System.out.println(cal.getTime()); //prints modified timestamp of 2017 (as expected)

//new instance
Calendar cal2 = Calendar.getInstance();
System.out.println(cal2.getTime()); //still prints current date object (as expected)

如果我在修改之前从之前的代码中克隆日历对象的第二个实例,我可以轻松修复我的测试。但这涉及修改现有的源代码,我不能,因为它是一个遗留代码。

那么我在使用这个静态方法的单元测试中做错了什么?我也尝试过使用 JMockit 进行测试,但仍然遇到同样的问题。很确定这是显而易见的事情,但无法弄清楚。谢谢你的时间。

那是因为myOwn是同一个实例。 thenReturn 代码在测试开始时仅 运行 一次。您只创建了一个 Calendar 对象,而 Mockito 只是 return 两次。因此,如果它在第一次调用 getInstance 时被修改,则修改后的对象将在第二次调用时被 returned。

试试看:

Calendar myOwn = //whatever
Calendar myOwnSecond = //whatever - new instance though, not the same as myOwn!!
when(Calendar.getInstance()).thenReturn(myOwn).thenReturn(myOwnSecond);

如果你想每次 return 一个新的日历实例(所以当你想要 x 个日历实例的时候),并且你不需要保留日历对于您的测试实例,您需要使用 thenAnswer。查看 here 了解详情。或者只使用:

when(Calendar.getInstance()).thenAnswer((invocation)->new MyCalendar());

或Java 7

when(Calendar.getInstance())
    .thenAnswer(new Answer<Calendar>() {
        Calendar answer(InvocationOnMock invocation) {
            return // however you're instantiating it
    }
});