1. 引言

在使用 JUnit 编写单元测试时,我们经常会看到两种失败类型:Failure(断言失败)Error(异常错误)

虽然它们都表示测试未通过,但语义上有明显区别:

Failure:表示预期断言未满足,说明代码逻辑有问题
Error:表示测试过程中出现了未处理的异常,比如空指针、除以零等

理解这两者的区别有助于我们更快定位问题所在,避免踩坑。

2. 示例代码

我们以一个简单的 SimpleCalculator 类为例,其中包含一个 divideNumbers 方法用于对两个 double 数进行除法运算:

public class SimpleCalculator {
    public static double divideNumbers(double dividend, double divisor) {
        if (divisor == 0) {
            throw new ArithmeticException("Division by zero!");
        }
        return dividend / divisor;
    }
}

⚠️ 注意:Java 在 double 类型除以零时并不会抛出异常,而是返回 InfinityNaN。我们这里手动添加了抛出异常的逻辑,是为了更直观地演示 Error 的情况。

3. 示例:Failure

当我们使用 JUnit 编写单元测试时,最常见的情况是测试失败(Failure)。这通常是因为代码没有达到预期结果,即断言未通过。

例如下面这个测试方法:

@Test
void whenDivideNumbers_thenExpectWrongResult() {
    double result = SimpleCalculator.divideNumbers(6, 3);
    assertEquals(15, result);
}

这段代码中,6 / 3 = 2,但我们在断言时期望结果是 15,因此测试失败,JUnit 会报告这是一个 Failure

✅ 这类问题通常说明你的业务逻辑有误,需要检查代码逻辑是否正确。

4. 示例:Error

除了断言失败外,还有一种情况是测试执行过程中发生了异常,导致测试无法正常完成,这类情况称为 Error

比如我们测试除以零的情况:

@Test
void whenDivideByZero_thenThrowsException() {
    SimpleCalculator.divideNumbers(10, 0);
}

此时会抛出一个 ArithmeticException,而测试并没有捕获它,JUnit 会将此标记为一个 Error

⚠️ 这类问题通常说明测试写得不够严谨,或者代码本身存在未处理的异常路径。

要修复这个问题,我们可以使用 assertThrows 来显式断言异常是否被正确抛出:

@Test
void whenDivideByZero_thenAssertException() {
    assertThrows(ArithmeticException.class, () -> SimpleCalculator.divideNumbers(10, 0));
}

✅ 此时如果抛出指定异常,测试通过;否则变成 Failure。

5. 总结

类型 含义 示例场景 说明
Failure 预期结果未满足 assertEquals(15, result) 失败 表示业务逻辑错误
Error 测试执行过程中发生异常 除以零抛出异常未处理 表示测试或代码存在未处理的异常

📌 简单粗暴总结一句话:

Failure 是“断言错了”,Error 是“出事了”。

两者都值得关注,Failure 更偏向逻辑问题,而 Error 更偏向运行时异常处理不全。

完整示例代码可参考 GitHub 仓库


原始标题:The Difference Between Failure and Error in JUnit