如何为“InterruptedException”编写单元测试

时间:2010-09-13 22:00:24

标签: java unit-testing

为了实现100%的代码覆盖率,我遇到了一种情况,我需要单独测试捕获InterruptedException的代码块。如何正确地测试这个? (请使用JUnit 4语法)

private final LinkedBlockingQueue<ExampleMessage> m_Queue;  

public void addMessage(ExampleMessage hm) {  
    if( hm!=null){
        try {
            m_Queue.put(hm);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

6 个答案:

答案 0 :(得分:15)

在调用addMessage()之前,请致电Thread.currentThread().interrupt()。这将在线程上设置“中断”状态标志。

如果在put()上调用LinkedBlockingQueue时设置了中断状态,即使InterruptedException无需等待,也会引发put (锁是无争用的)。

顺便说一下,达到100%覆盖率的一些努力会适得其反,实际上会降低代码质量。

答案 1 :(得分:12)

使用像Easymock这样的模拟库并注入一个模拟的LinkedBlockingQueue

@Test(expected=InterruptedException.class)
public void testInterruptedException() {
    LinkedBlockingQueue queue = EasyMock.createMock(LinkedBlockingQueue.class);
    ExampleMessage message = new ExampleMessage();
    queue.put(message);
    EasyMock.expectLastCall.andThrow(new InterruptedException()); 
    replay(queue);
    someObject.setQueue(queue);
    someObject.addMessage(msg);
}

答案 2 :(得分:1)

另一种选择是委托处理InterruptedException Guava Uninterruptibles,因此您无需为其编写和测试自定义代码:

import static com.google.common.util.concurrent.Uninterruptibles.putUninterruptibly;

private final LinkedBlockingQueue<ExampleMessage> queue;  

public void addMessage(ExampleMessage message) {  
    putUninterruptibly(queue, message);
}

答案 3 :(得分:0)

一种正确的方法是为执行器服务定制/注入ThreadFactory,并从线程工厂中获得创建的线程的句柄,然后您可以安排一些任务来中断感兴趣的线程。

ThreadFactory中的覆盖方法“newThread”的演示代码部分:

ThreadFactory customThreadfactory new ThreadFactory() {

            public Thread newThread(Runnable runnable) {
                final Thread thread = new Thread(runnable);
                if (namePrefix != null) {
                    thread.setName(namePrefix + "-" + count.getAndIncrement());
                }
                if (daemon != null) {
                    thread.setDaemon(daemon);
                }
                if (priority != null) {
                    thread.setPriority(priority);
                }

                scheduledExecutorService.schedule(new Callable<String>() {
                    public String call() throws Exception {
                        System.out.println("Executed!");
                        thread.interrupt();
                        return "Called!";

                    }
                },
                5,
                TimeUnit.SECONDS);

                return thread;
            }
        }

然后您可以使用以下内容构建您的executorservice实例:

ExecutorService executorService = Executors.newFixedThreadPool(3,
        customThreadfactory);

然后在5秒后,中断信号将被发送到线程,每个线程将在执行器服务中被中断一次。

答案 4 :(得分:0)

如上所述,如果您抓住了Thread.currentThread().interrupt()并且不会将其扔掉,请使用InterruptedException

关于单元测试。以这种方式进行测试:Assertions.assertThat(Thread.interrupted()).isTrue();。它既检查线程是否被中断,又清除中断标志,以免破坏其他测试,代码覆盖率或下面的任何内容。

答案 5 :(得分:0)

问题中的示例代码可以通过调用 Thread.currentThread().interrupt() 进行测试。但是,除了上述问题之外,还有各种方法会重置 interrupted 标志。例如,这里有一个广泛的列表:https://stackoverflow.com/a/12339487/2952093。可能还有其他方法。

假设应测试如下实现的等待:

try {
  TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException ex) {
    // Set the interrupt flag, this is best practice for library code
    Thread.currentThread().interrupt();
    throw new RuntimeException(ex);
}

调用 Thread.sleep 本身会清除 interrupted 标志,因此不能提前设置。可以使用自己的测试线程进行测试,如下所示:

AtomicBoolean threadInterrupted = new AtomicBoolean(false);
Runnable toBeInterrupted = () -> {
    try {
        methodUnderTest();
    } catch (RuntimeException unused) {
        // Expected exception
        threadInterrupted.set(true);
    }
};

// Execute the in an operation test thread
Thread testThread = new Thread(toBeInterrupted);
testThread.start();

// When the test thread is waiting, interrupt
while (!threadInterrupted.get()) {
    if (testThread.getState() == Thread.State.TIMED_WAITING) {
        testThread.interrupt();
    }
}

// Assert that the interrupted state is re-set after catching the exception
// Must be happening before thread is joined, as this will clear the flag
assertThat(testThread.isInterrupted(), is(true));
testThread.join();