我已经看过很多关于 JUnit/Mockito 中基于时间的测试技术的帖子,但似乎有太多的考虑因素,以至于我对应该如何看待我自己的被测试代码以及如何/什么感到困惑正是我应该测试的。
我的测试代码如下:
class ClassUnderTest {
Clock clock; // Thought this might be a useful DI, but not sure how...
String delayString;
Timer timer;
public ClassUnderTest(String delayString, Clock clock) {
this.clock = clock;
this.delayString = delayString;
init();
}
private void init() {
ChronoUnit unit = parseDelayStringToGetUnit(); // Implementation not shown here
Integer amountOfTime = parseDelayStringToGetAmount(); // Implementation not shown here
long delay = calculateDelay(unit, amountOfTime); // Implementation not show here
timer.schedule(new TimerTask() {
@Override
public void run() {
taskToRun();
}
}, delay);
}
private void taskToRun() {
// Does something after a delay
// Happy to amend signature to take params or return a value ...
}
}
诚然,它被削减了很多,但我想删掉不重要的东西。它的基本功能是在实例化时调用 taskToRun(),但只有在解析传递的另一个字符串之后才能调用,并且该字符串可以反序列化为 ChronoUnit 和关联的金额。传递的延迟长度可能有限,但范围可能从 15 分钟到 8 小时不等。
我读到我不需要测试计时器本身,但我仍然觉得我的测试方法应该确认 taskToRun() 确实最终运行,但不是在所需的延迟到期之前运行。我认为传递 java.time.Clock 作为依赖注入将有助于基于时间的测试,但目前我看不到如何(此处)并且显然它还没有用于任何用途。
我想要测试的原则上是否正确?如果是,我该如何去做?
编辑:
抱歉。刚刚意识到delayString还包含“epochStart”,即延迟应该开始的时刻。当前对原始“延迟”的计算将改为计算:
instantWhenDelayExpires = epochStart +(ChronoUnit 数量)
然后计时器将使用 instantWhenDelayExpires 而不是延迟量进行安排。
我不认为这让我的问题特别复杂?
但我仍然觉得我的测试方法应该确认taskToRun() 确实最终会运行,但不会在所需的延迟到期之前运行
确实如此,为了验证为什么你不依赖
Timer.schedule(TimerTask task, long delay)
指定延迟?ClassUnderTest
中为您的测试添加一个接受 Timer
的构造函数,并且您可以在单元测试期间模拟它。
要断言 Timer
能够实现其设计目的,您只需验证是否使用正确的参数(尤其是延迟参数)调用了模拟。
@Mock
Timer mockedTimer;
@Test
void constructor(){
long expectedDelay = ...;
ClassUnderTest objectUnderTest = new ClassUnderTest(..., mockedTimer);
Mockito.verify(mockedTimer).schedule(Mockito.any(), expectedDelay);
}
您可以在应用程序配置中定义 bean,
@Bean
public Timer timer()
{
return new Timer();
}
将该 bean 自动装配到类中,
@Autowired
private Timer _timer;
并在您的测试类中模拟该 bean,
@MockBean
private Timer _timer;
然后您可以捕获进入计时器的任务并验证运行任务是否执行您想要的操作。
ArgumentCaptor<TimerTask> timerCaptor = ArgumentCaptor.forClass(TimerTask.class);
Mockito.verify(_timer, Mockito.times(1))
.scheduleAtFixedRate(timerCaptor.capture(), Mockito.anyLong(), Mockito.anyLong());
TimerTask timerTask = timerCaptor.getValue();
Assertions.assertNotNull(timerTask);
// assuming the time runs the task
timerTask.run();
// verify methods inside run task are invoked...