如何测试此方法';Java中的预期行为:它生成一个线程,并在特定条件下抛出异常
假设我有一个生成新线程并执行一些工作的方法。在某些条件下,新生成的线程将抛出某种类型的异常,从而终止整个进程。我想编写JUnit测试来验证这种行为。有办法吗 方法是:如何测试此方法';Java中的预期行为:它生成一个线程,并在特定条件下抛出异常,java,multithreading,exception-handling,Java,Multithreading,Exception Handling,假设我有一个生成新线程并执行一些工作的方法。在某些条件下,新生成的线程将抛出某种类型的异常,从而终止整个进程。我想编写JUnit测试来验证这种行为。有办法吗 方法是: private void foo() { new Thread() { @Override void run() { throw new CertainException("exception messages"); } }.start(); } 在测试中(概念上): 此测试不起作用,因为无法
private void foo() {
new Thread() {
@Override void run() {
throw new CertainException("exception messages");
}
}.start();
}
在测试中(概念上):
此测试不起作用,因为无法捕获从另一个线程生成的异常。如果您只想测试run()方法内部的代码,请将其重构为foo()方法的ouf(可能是可运行的),然后单独测试,而不必从线程运行它
private void foo() {
new Thread(new MyRunnable()).start();
}
public class MyRunnable implements Runnable {
public void run() {
....
}
}
现在,您可以实例化MyRunnable对象并从测试中调用run()方法,而无需启动线程
private void foo() {
new Thread(new MyRunnable()).start();
}
public class MyRunnable implements Runnable {
public void run() {
....
}
}
编辑
线程创建的测试可以通过使用ThreadFactory模拟来完成。(正如Jon Skeet所指出的)。那么,单元测试应该验证方法调用的结果,而不是实现细节 在您的库中,若线程终止,它将如何影响库用户?也许计算不会完成,最终结果也不会记录在数据库中?然后检查数据库。也许线程会停止执行某些定期任务(如清理)?然后检查是否仍在进行清理
若抛出的异常不会以任何方式影响用户,那个么就并没有什么可检查的了。因为是否引发异常只是一个实现细节(用户将永远看不到它)。一个选项是将启动线程的功能设置为依赖项,您可以使用现有的
ThreadFactory
接口指定。然后在单元测试中,您可以提供一个专门的ThreadFactory
,它包装给定的Runnable
,以便记录异常等
您将能够测试:
- 使用了
ThreadFactory
- 线程已启动
- 该操作引发了一个异常
UncaughtExceptionHandler
。每当线程抛出异常时,就会调用它。在此处理程序中,您可以检查预期异常是否等于引发的异常,例如,测试消息或统计异常的发生次数。通过使用倒计时闩锁
,您还可以检查是否及时抛出异常,以及预期的异常数量
即使您无权访问由被测类创建的线程
,此功能也能正常工作。但是,如果您可以访问它,肯定有一种更简单的方法,例如重构被测类并引入异常侦听器或类似的方法。使被测类具有更好的可测试性还可以改进设计,例如,通过删除对线程的依赖,并直接测试可以外部化的run()方法的主体
public class ThreadExceptionTest {
private void foo() {
new Thread(new Runnable() {
@Override
public void run() {
throw new RuntimeException("exception messages");
}
}).start();
}
@Test
public void testFoo() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
final RuntimeException expectedException = new RuntimeException("exception messages");
UncaughtExceptionHandler eh = new UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
if (e.getMessage().equals(expectedException.getMessage()))
latch.countDown();
}
};
Thread.setDefaultUncaughtExceptionHandler(eh);
foo();
assertTrue(latch.await(100,TimeUnit.MILLISECONDS));
}
}我同意有些事情应该通过单元测试来完成,而有些事情不应该。假设我在做TDD,那么需要有一种方法通过测试来定义这种行为,而不一定是传统的单元测试。我想通过测试定义的规范是:“当出现问题时,系统应该失败,并出现特定的异常和特定的消息。”感谢您的解决方案。但是,我仍然不确定更改每个线程的未捕获异常处理程序的全局状态是否正确,这仅仅是基于我认为测试不应该有副作用的信念。获取旧的,保存它,然后在最终{}中重置它。无论如何,关于副作用你是对的。您是否能够将测试中的类更改为引入ThreadFactory,或者更好地,引入作业/任务抽象?