为了实现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();
}
}
}
答案 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();