【问题标题】:How to capture all uncaucht exceptions on junit tests?如何在junit测试中捕获所有未捕获的异常?
【发布时间】:2016-04-15 13:18:53
【问题描述】:

如果我们创建了一个单例对象来处理 Java 异常,为什么 Thread.setDefaultUncaughtExceptionHandler 在 Java 应用程序服务器、Java 控制台应用程序中运行正常但在 JUnit 测试中不起作用?

例如,以下代码有效:

public class Main extends Object {

    public static void main(String[] arguments) {
        Thread.setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler.getInstance());
        double a = 1/0;
    }
}

但是这个 JUnit 测试不是:

public class UncaughtExceptionHandlerTest {

    @Test
    public void throwException() {
        Thread.setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler.getInstance());
        double a = 1/0;
    }
}

但是为什么呢?而且,我们如何解决这个问题,自动处理所有 JUnit 测试异常,而不对每个测试使用喜怒无常的 try catch?

【问题讨论】:

  • 不理解反对票。主要动机是,例如,如果 junit 测试失败,则发送电子邮件或执行其他管理任务。如果我有一个全局异常处理程序,我可以这样做,而不是在每个测试中放置一个 catch 块。处理完之后,也许我会抛出这个异常,让junit照着做。

标签: java junit exception-handling junit4


【解决方案1】:

JUnit 将捕获所有单元测试线程上的单元测试抛出的意外异常1。正常行为是将异常捕获/显示/记录为 FAILed 测试,然后继续下一个单元测试。

这意味着没有 Java 意义上的“未捕获异常”,并且您的未捕获异常处理程序不会被调用。

尚不完全清楚您要在这里实现什么,但我怀疑答案是实现自定义运行器:


1 - 如果被测代码产生自己的线程,JUnit 框架无法知道。它肯定不能在这些线程上捕获/检测未捕获的异常。但是,这似乎不是您在这个问题中所说的。


主要动机是,例如,如果 junit 测试失败,则发送电子邮件或执行其他管理任务。如果我有一个全局异常处理程序,我可以这样做,而不是在每个测试中放置一个 catch 块。处理完之后,也许我会抛出这个异常,让junit照着做。

如果这就是你想要做的,那么你 (IMO) 做错了。现有的运行程序提供结构化报告文件或报告数据结构,可以为您提供所有通过的测试列表、断言失败、异常失败等。您应该做的是:

  • 选择合适的跑步者
  • 分析其输出
  • 如果出现符合您标准的错误,请发送一封电子邮件(或其他任何内容)。

优点:

  • 省力
  • 您处理所有错误,而不仅仅是未捕获的异常(尽管实际上断言失败表现为 AssertionError 异常 ...)
  • 您不会在每次失败的测试中都向自己发送垃圾邮件。

还有另一种方法。看看JUnitCore (link)。这允许您为各种测试事件注册一个侦听器,然后运行一堆测试或测试套件。

另一点是您似乎试图复制(部分)持续集成 (CI) 服务器(例如 Jenkins)的功能。


然后你问为什么这不起作用:

@Test
public void throwException() {
    Thread.setDefaultUncaughtExceptionHandler(/* some handler */));
    double a = 1/0;
}

只有在没有其他方法捕获异常时才会调用未捕获的异常处理程序。但是一个典型的 JUnit 测试运行程序使用传统的异常处理程序捕获从每个单元测试传播的所有异常。这意味着在您的测试中抛出的ArithmeticException 永远不会到达您的处理程序。

【讨论】:

  • 说得好——这就是我想要表达的;)
  • 我去看看。我把动机放在上面。我会让 junit 执行它的正常任务,但是,我想拦截它并执行我的一些任务。
  • 在这个链接stackoverflow.com/questions/3163117/…,我正在做的应该是可能的。但不是这个实现,我不想要与 ExceptionHandler 类相同的测试类。但是我不明白为什么上面的代码不起作用。
  • 嗯... JUnit 肯定不会捕获所有可能抛出的异常;如果你在你的代码产生的线程上运行,你仍然需要注意它们是否会爆炸。 JUnit 只负责运行 @Test 注释方法的线程。
【解决方案2】:

junit @Test 方法抛出的异常并非未被捕获。 JUnit 会捕获它们并使用它们来使您的测试失败。

现在,如果您自己启动了一个不在 JUnit 的 try/catch 执行中运行的新线程,则抛出的异常将基本上被忽略并且您的测试将通过。

想想这个名字……Thread.setDefaultUncaughtExceptionHandler。这仅涵盖没有明确具有未捕获异常处理程序的线程,然后它不涵盖由调用您的代码的代码(JUnit 等)捕获的异常。

这是 ParentRunner 类的相关代码:

    protected final void runLeaf(Statement statement, 
                                 Description description, RunNotifier notifier) {
    EachTestNotifier eachNotifier = new EachTestNotifier(notifier, description);
    eachNotifier.fireTestStarted();
    try {
        statement.evaluate();
    } catch (AssumptionViolatedException e) {
        eachNotifier.addFailedAssumption(e);
    } catch (Throwable e) {
        eachNotifier.addFailure(e);
    } finally {
        eachNotifier.fireTestFinished();
    }

【讨论】:

  • 仅部分正确。 JUnit 可以捕获子线程中抛出的异常,并且会使当前运行的测试用例失败,即使它没有产生那个线程。
  • 不确定你在说什么版本的junit,但至少junit 4你的评论根本不正确。 imgur.com/a/8Fk7bpJ 打印异常,测试通过。
【解决方案3】:

您确定 jUnit 没有在某处捕获它吗?方法签名说它是throws Exception,所以我猜上游必须有一个相当广泛的catch声明。

【讨论】:

  • 投掷是在我的测试中。这是不必要的,因为这是一个 RuntimeException。如果异常被捕获,那么在调试模式下,它将在 UncaughtExceptionHandler 类的断点处停止,就像在 Java 控制台应用程序中一样。
猜你喜欢
  • 2020-05-13
  • 1970-01-01
  • 2021-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-11
  • 2015-03-16
相关资源
最近更新 更多