有没有办法每次使用 EasyMock return anyTimes() 来 return 一个新对象?

Is there a way to return a new object each time with EasyMock return anyTimes()?

我正在为严重依赖日历的 class 进行单元测试。我正在尝试模拟所有调用,以便基本上可以将我自己指定的系统时间插入 class 以进行一些日期时间逻辑测试。

这是我的第一次尝试:

Calendar fixedSystemCal = Calendar.getInstance();
fixedSystemCal.set(2021, Calendar.FEBRUARY, 22, 18, 0, 0);

PowerMock.mockStatic(Calendar.class);
EasyMock.expect(Calendar.getInstance()).andReturn(fixedSystemCal).anyTimes();
PowerMock.replay();

不幸的是,经过一番摸索,我发现这行不通,因为日历对象在 class 中发生了变化。由于此模拟 returns 每次都使用相同的日历,因此这是一个问题。例如:

Calendar firstCall = Calendar.getInstance(); //Feb 22
firstCall.add(Calendar.DATE, 5); //Feb 27

Calendar secondCall = Calendar.getInstance() // This is also Feb 27 now

有没有什么办法每次都返回不同的日历对象(对于任意数量的调用)?到目前为止我找到的唯一解决方案就是像这样重复多次:

Calendar fixedSystemCal = Calendar.getInstance();
fixedSystemCal.set(2021, Calendar.FEBRUARY, 22, 18, 0, 0);

PowerMock.mockStatic(Calendar.class);
EasyMock.expect(Calendar.getInstance()).andReturn((Calendar) fixedSystemCal.clone()).andReturn((Calendar) fixedSystemCal.clone()).andReturn((Calendar) fixedSystemCal.clone());
PowerMock.replay();

我将在下一步中用 Java 时间替换此代码,但我需要进行功能单元测试才能进行更改。

您应该能够使用 andAnswer 而不是 andReturn 来提供可用于 return 新实例的 lambda/IAnswer 实现。类似于:

EasyMock.expect(Calendar.getInstance())
   .andAnswer(() -> (Calendar)fixedSystemCal.clone());

您需要使用 andAnswer 而不是 andReturn。尽管它们的名字看起来很相似,但在模拟和测试期间它们都有不同的用途。如果您需要使用静态值(总是同一个实例),请使用 andReturn。如果您需要使用动态值或派生值,请使用 andAnswer。

代码示例

Calendar fixedSystemCal = Calendar.getInstance();
fixedSystemCal.set(2021, Calendar.FEBRUARY, 22, 18, 0, 0);

PowerMock.mockStatic(Calendar.class);
EasyMock.expect(Calendar.getInstance()).andReturn((Calendar) fixedSystemCal.clone()).andReturn((Calendar) fixedSystemCal.clone()).andReturn((Calendar) fixedSystemCal.clone());
PowerMock.replay();

EasyMock.expect(Calendar.getInstance()).andAnswer(() -> (Calendar) fixedSystemCal.clone());

并返回

andReturn 设置调用方法时要 returned 的值。该值将始终 与您定义的同一实例。在您的情况下,它将 return fixedSystemCal.clone() 的值,这将始终是您克隆一次的日历的同一实例。将其视为将该克隆保存在一个变量中,并且在调用该方法时总是 returning 同一个变量。

只有在模拟方法调用时知道 return 值时才应使用 andReturn。当您调用模拟方法时,此预定义值将被 returned。

如果你想用常规代码表示。模拟一个方法 getDate 看起来像这样,你总是 return 日历的相同实例:

private Calendar calendar = Calendar.getInstance();

public Calendar getCalendar() {
    return calendar;
}

和Anwser

andAnwser 将在 return 值之前执行一个方法。当您需要在调用模拟方法时执行 其他操作 时,可以使用此方法,例如当你需要克隆一个对象或者你想根据传递的参数执行一些逻辑时。

如果你想用常规代码表示。模拟一个方法 getDate 看起来像这样,你总是 return 一个新的日历实例:

public Calendar getCalendar() {
    return Calendar.getInstance();
}