我想测试一个以串行方式执行任务并抛出的服务如果某个任务等待超过5秒,则为TimeOutInServiceException
。我想测试超时场景。
我写的完整的测试场景相当复杂,我正在寻找最简单的解决方案。
测试这种服务的关键点是
- 服务超时由于异常(不是返回一些特殊值)
- 我们必须在单独的线程中启动更长的阻塞任务
- 我们不能通过异常
@Test(expected = ...)
退出测试,因为在其他测试期间,较长的任务线程仍然在后台阻塞服务中存活
那么,我当前的测试用例是这样的:
- create
Executor
- 创建长效
Callable
/Runnable
-
.submit()
long task to executor - 休眠一段时间,以确保较长的任务先启动
-
.submit()
超时失败任务 -
.shutdown()
执行器和.awaitTermination()
-
.get()
和.assertTrue()
第一个任务完成OK 在
.get()
失败时捕获ExecutionException
,并将原因存储在prepared ref:Exception thrown = null; try { timeoutedTaskFuture.get(); } catch ( ExecutionException ex ) { thrown = (Exception)ex.getCause(); }
检查预期异常类型:
assertTrue( thrown instanceof TimeOutInServiceException );
Callable
/Runnable
是否有更好的方法来测试这种场景?
如果您的代码严重依赖于Lock功能,那么我会考虑引入某种结构来为您的代码提供Lock的实例。然后,您可以将您的提供者/供应商替换为一个构造,以返回Lock的模拟实例。这将允许您测试特定的条件,而不必创建复杂的单元测试。
使用Google Guava,你可以在JMock中这样做:
// this class supplies real Lock instances.
public class LockSupplier implements Supplier<Lock> {
@Override
public Lock get() {
return new ReentrantLock();
}
}
// this class supplies your mocked instances for your unit test.
public class MockLockSupplier implements Supplier<Lock> {
private Lock mockedLock;
public MockLockSupplier(Mockery context) {
mockedLock = context.mock(Lock.class);
}
@Override
public Lock get() {
return mockedLock;
}
}
在测试用例中设置了锁供应商之后,接下来就是在JMock中设置期望了。