如何在 Junit 中测试 ScheduledExecutorService 异常处理?
How to test ScheduledExecutorService exception handling in Junit?
我有一个任务,我计划每 30 分钟 运行。我使用 ScheduledExecutorService 来安排。
我想测试 (junit) ScheduledExecutorService 的异常处理,这样无论何时抛出异常,线程都不会因为异常而死亡。
我的代码:
public enum MonitorTask {
TIMER;
private final AtomicBoolean isPublishing = new AtomicBoolean(false);
private final long period = 18000000
public synchronized boolean initialize() {
return initialize(period, period);
}
/**
* @return true, if call was successful i.e. Timer task was scheduled
*/
boolean initialize(long delay, long period) {
if (isPublishing.get()) {
log.warn("Already monitoring for new feature data");
return false;
}
//execute on daemon thread
ScheduledExecutorService scheduledExecutorService =
Executors.newSingleThreadScheduledExecutor(runnable -> {
Thread thread = new Thread(runnable);
thread.setDaemon(true);
return thread;
}
);
Runnable runnableTask = () -> {
try {
DataPublisher.INSTANCE.update(DateTime.now());
} catch (Throwable e) {
log.warn("Failed to check for new Data!", e);
}
};
scheduledExecutorService.scheduleAtFixedRate(runnableTask, delay, period, TimeUnit.MILLISECONDS);
isPublishing.set(true);
return true;
}
}
至于现在,我的单元测试检查功能:
public class MonitorTaskTest {
@Test
public void testInitialize() throws Exception {
AtomicInteger val = new AtomicInteger(0);
DataProvider provider = testProvider(val);
assertEquals(0, val.get());
// this should update val every 10 ms ( adds 1 to val )
Assert.assertTrue(MonitorTask.TIMER.initialize(0, 10));
assertEquals(0, val.get());
DataPublisher.INSTANCE.registerForNewData(provider, DateTime.now());
// wait for 3 updates
Thread.sleep(10 * 3);
Assert.assertTrue("Expected val to be >= 3 but is " + val.get(), val.get() >= 3);
}
@Before
public void setUp() {
DataPublisher.INSTANCE.clear();
}
private static DataProvider testProvider(final AtomicInteger ai) {
return new DataProvider() {
private AtomicInteger val = ai;
@Override public boolean update(DateTime dateTime) throws Exception {
val.incrementAndGet();
return true;
}
@Override public boolean exists(DateTime dateTime) {
return true;
}
@Override public void close() throws Exception {
}
};
}
}
我认为您在这里走错了兔子洞。意思是:当你检查你正在使用的方法的 javadoc 时,你会发现:
Creates a single-threaded executor that can schedule commands to run after a given delay, or to execute periodically. (Note however that if this single thread terminates due to a failure during execution prior to shutdown, a new one will take its place if needed to execute subsequent tasks.)
换句话说:你问的是如何测试保证由你正在使用的Java系统库工作的东西。从这个意义上说,你是在浪费时间。
您可能宁愿花时间改进您的代码以使其更易于测试。你看 - 当你的 class 将 接收 一个 ExecutorService 对象(而不是为自己创建一个)时,你可以为你的单元测试传递一个 same thread executor 。突然之间,您的单元测试可以 运行 在 一个 线程上进行,这使得整个测试变得容易得多 - 因为它允许您摆脱 sleep 测试中的语句。 (尽管系统库保证您这样做,但这些睡眠语句 比线程未重新启动的可能性 问题多得多 。
除此之外:您的 运行nable 已经以一种方式编写,应该 保证 线程 运行 宁此代码 never die(当然是questionable去抓Throwable)。但是为了测试它,我猜你 只 需要另一个“测试提供者”,其中 update()
抛出任何类型的异常。
我有一个任务,我计划每 30 分钟 运行。我使用 ScheduledExecutorService 来安排。
我想测试 (junit) ScheduledExecutorService 的异常处理,这样无论何时抛出异常,线程都不会因为异常而死亡。
我的代码:
public enum MonitorTask {
TIMER;
private final AtomicBoolean isPublishing = new AtomicBoolean(false);
private final long period = 18000000
public synchronized boolean initialize() {
return initialize(period, period);
}
/**
* @return true, if call was successful i.e. Timer task was scheduled
*/
boolean initialize(long delay, long period) {
if (isPublishing.get()) {
log.warn("Already monitoring for new feature data");
return false;
}
//execute on daemon thread
ScheduledExecutorService scheduledExecutorService =
Executors.newSingleThreadScheduledExecutor(runnable -> {
Thread thread = new Thread(runnable);
thread.setDaemon(true);
return thread;
}
);
Runnable runnableTask = () -> {
try {
DataPublisher.INSTANCE.update(DateTime.now());
} catch (Throwable e) {
log.warn("Failed to check for new Data!", e);
}
};
scheduledExecutorService.scheduleAtFixedRate(runnableTask, delay, period, TimeUnit.MILLISECONDS);
isPublishing.set(true);
return true;
}
}
至于现在,我的单元测试检查功能:
public class MonitorTaskTest {
@Test
public void testInitialize() throws Exception {
AtomicInteger val = new AtomicInteger(0);
DataProvider provider = testProvider(val);
assertEquals(0, val.get());
// this should update val every 10 ms ( adds 1 to val )
Assert.assertTrue(MonitorTask.TIMER.initialize(0, 10));
assertEquals(0, val.get());
DataPublisher.INSTANCE.registerForNewData(provider, DateTime.now());
// wait for 3 updates
Thread.sleep(10 * 3);
Assert.assertTrue("Expected val to be >= 3 but is " + val.get(), val.get() >= 3);
}
@Before
public void setUp() {
DataPublisher.INSTANCE.clear();
}
private static DataProvider testProvider(final AtomicInteger ai) {
return new DataProvider() {
private AtomicInteger val = ai;
@Override public boolean update(DateTime dateTime) throws Exception {
val.incrementAndGet();
return true;
}
@Override public boolean exists(DateTime dateTime) {
return true;
}
@Override public void close() throws Exception {
}
};
}
}
我认为您在这里走错了兔子洞。意思是:当你检查你正在使用的方法的 javadoc 时,你会发现:
Creates a single-threaded executor that can schedule commands to run after a given delay, or to execute periodically. (Note however that if this single thread terminates due to a failure during execution prior to shutdown, a new one will take its place if needed to execute subsequent tasks.)
换句话说:你问的是如何测试保证由你正在使用的Java系统库工作的东西。从这个意义上说,你是在浪费时间。
您可能宁愿花时间改进您的代码以使其更易于测试。你看 - 当你的 class 将 接收 一个 ExecutorService 对象(而不是为自己创建一个)时,你可以为你的单元测试传递一个 same thread executor 。突然之间,您的单元测试可以 运行 在 一个 线程上进行,这使得整个测试变得容易得多 - 因为它允许您摆脱 sleep 测试中的语句。 (尽管系统库保证您这样做,但这些睡眠语句 比线程未重新启动的可能性 问题多得多 。
除此之外:您的 运行nable 已经以一种方式编写,应该 保证 线程 运行 宁此代码 never die(当然是questionable去抓Throwable)。但是为了测试它,我猜你 只 需要另一个“测试提供者”,其中 update()
抛出任何类型的异常。