如何在 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() 抛出任何类型的异常。