如何在Junit中测试ScheduledExecutorService异常处理?

时间:2017-08-01 00:35:13

标签: java multithreading junit scheduledexecutorservice

我有一项任务,我计划每隔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 {

            }
        };
    }
}

1 个答案:

答案 0 :(得分:2)

我想你在这里走错了兔子洞。含义:当您检查javadoc所使用的方法时,您会发现:

  

创建一个单线程执行程序,可以调度命令在给定的延迟后运行,或定期执行。 (但请注意,如果此单个线程由于在关闭之前执行期间的故障而终止,则在需要执行后续任务时将使用新的线程。)

换句话说:您正在询问如何测试正在使用的Java系统库保证的内容。从这个意义上说,你在浪费你的时间。

可能宁愿花时间改进你的代码,以便更容易测试。你知道 - 当你的类接收一个ExecutorService对象(而不是为自己创建一个)时,你可以为你的单元测试传入same thread executor。突然间,您的单元测试可以在一个线程上运行,这使得整个测试变得更加容易 - 因为它可以让您摆脱你的睡眠语句试验。 (并且那些睡眠语句更多的问题比线程没有重新启动的可能性,尽管系统库保证你这样做。)

除此之外:您的runnable已经以保证的方式编写,运行此代码的线程从不死亡(当然,它是可疑的抓住 Throwable )。但是为了测试它,我猜你需要另一个“测试提供者”,其中update()抛出任何类型的异常。