【问题标题】:JUnit / Mockito: throwing an exception in a new threadJUnit / Mockito:在新线程中抛出异常
【发布时间】:2020-05-22 02:55:40
【问题描述】:

我正在为以下 Java 代码编写单元测试:

try {
    new Thread(() -> myService.myMethod()).start();
    return new MyResponse("Done");
} catch (Exception exception) {
    throw new MyException("Internal Error", exception);
}

我正在尝试模拟 myService.myMethod() 引发的错误,以便在上面代码的 catch 块中拾取它并引发 MyException 的实例。

在我的 JUnit 测试中(使用 org.springframework.test.context.junit4.SpringJUnit4ClassRunner 和 Mockito),我模拟了 MyService 并将其作为 Spring bean 注入到单元测试中:

MyService myService = Mockito.mock(MyService.class);

然后我尝试了这个来模拟服务抛出异常:

IllegalArgumentException myException = new IllegalArgumentException();
Mockito.doThrow(myException).when(myService).myMethod();

...但是我正在测试的方法的 catch 块中永远不会发现异常。

这可能与代码使用另一个线程来调用 myService.myMethod() 的事实有关。

有什么方法可以让 myService.myMethod() 抛出的异常被正确捕获?

提前感谢您的帮助。

【问题讨论】:

  • 如果方法在启动线程后返回,然后在线程中稍后发生异常,您希望代码会发生什么?
  • 如果线程中的进程抛出异常,它应该被catch()捕获,不是吗?
  • 不,只有当异常显示在 catch 块中的触发时才会被捕获。您正在创建一个新线程,该线程将在未来某个时间点运行,而当前线程将继续返回,然后退出 catch 块。您可以将uncaughtExceptionHandler 附加到新线程。

标签: java spring junit mockito


【解决方案1】:

“外部”线程可以在另一个线程中捕获异常是没有意义的。 如果您在代码中添加上下文,例如

Thread th = new Thread(() -> { Thread.sleep(10000); throw new RuntimeException(); });
try { th.start(); } catch (Exception e) {
    System.out.println("uh-oh");
}
for (int i = 0; i < 1000; i++){
    System.out.println("running " + i);
    Thread.sleep(1000);
}

线程已启动,10 秒后将抛出异常。然而,开始线程已经开始每秒计数。如果启动线程的异常会被捕获,您是否希望主线程回到它捕获异常的地方?它应该中断计数然后继续吗?不继续?

您能做的最好的事情可能是指定一个错误处理程序(就像 Roger 在他的评论中提到的那样),但是您必须每隔一段时间从主线程查询它的状态,因为它将在并发启动的线程中运行。

考虑到t = new Thread(); t.start(); 不建议再使用,最简单的方法是使用CompletableFuture IMO:

CompletableFuture<Void> future = 
    CompletableFuture.runAsync(() -> myService.myMethod());

现在您可以稍后查询发生了什么:

if (future.isDone()) // task has finished in any way

if (future.isDone() && future.isCompletedExceptionally()) // task was finished because an exception was thrown

【讨论】:

    【解决方案2】:

    你的代码执行顺序如何?首先你需要设置你的模拟,然后运行它。

    MyService myService = Mockito.mock(MyService.class);
    Mockito.when(myService.myMethod()).doThrow(myexception.class); // try in this order
    
    
    try {
        new Thread(() -> myService.myMethod()).start();
        return new MyResponse("Done");
    } catch (Exception exception) {
        throw new MyException("Internal Error", exception);
    }
    

    顺便说一句,如果您使用的是 java 8 或更高版本,您可能需要使用 CompletableFuture 包。即CompletableFuture.supplyAsync(() -&gt; {})

    【讨论】:

    • myService.myMethod 的返回类型是 void,所以很遗憾我不能使用你的“Mockito.when()”语法。
    • 您是否尝试过在 lambda 中使用 Mockito.mock。 ?
    • 我该怎么做呢?
    猜你喜欢
    • 1970-01-01
    • 2014-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多