JUnit 预期异常测试:@Test(expected)

⚡ 智能摘要

JUnit 异常测试使用 @Test 注解的可选 expected 参数来确认方法是否抛出了它应该抛出的错误。 JUnit 4 和 assertThrows 方法 JUnit 5.

  • 🔘 目的: 证明无效输入会产生文档中记录的异常,而不是静默地给出错误答案。
  • ☑️ 语法: @Test(expected = ArithmeticException.class) 仅当抛出该特定类型的异常时才通过。
  • ✅ 计费示例: JUnit消息除以零,AirthematicTest 预期会引发 ArithmeticException 异常。
  • 🧪 另一种方法: try 代码块后跟 fail() 函数,可以让你在断言之前检查捕获到的异常。
  • 🛠️ JUnit 5: assertThrows 返回捕获到的异常,因此也可以断言异常消息和异常原因。
  • 📌 陷阱: 无论异常发生在哪里,预期参数都会传递,这可能会掩盖设置代码中的错误。

JUnit 使用 @Test(expected) 参数进行预期异常测试

JUnit 提供以下设施 trac抛出异常,并检查代码是否抛出了预期的异常。

JUnit 4 提供了一种简单易懂的异常测试方法。您可以使用:

  • 可选参数(预期) @Test 注解和
  • 至 trac可以利用这些信息,“fail()”函数。

而 测试 如果出现异常,您需要确保您在可选参数中提供的异常类是正确的。 @Test 注解 这与该方法实际抛出的异常相同。这是因为你期望该方法抛出一个异常。 单元测试否则我们的 JUnit 测试将会失败。

示例:@Test(expected = IllegalArgumentException.class)

通过使用“expected”参数,您可以指定测试可能抛出的异常名称。在上面的示例中,您使用的是“非法参数异常“,如果开发者使用了不允许的参数,则该测试会抛出此异常。

使用 @Test(expected) 的示例

让我们通过创建一个异常测试实例来理解异常测试。 Java 类中有一个方法会抛出异常 例外你将在一个测试类中处理并测试它。请考虑 JUnitMessage.java 文件中有一个执行数学运算的方法。第 14 行的除法运算除以了零,因此该方法总是抛出“ArithmeticException”异常。请参见下文:

JUnit消息类 Eclipse 第 14 行的 printMessage 函数除以了零

package guru99.junit;

public class JUnitMessage{

	private String message;

	public JUnitMessage(String message) {
		this.message = message;
	}

public void printMessage(){

	System.out.println(message); 
	int divide=1/0;

}

public String printHiMessage(){ 

	message="Hi!" + message;
	
	System.out.println(message);

	return message;
}

}

Code 说明:

  • Code 线7: 创建具有字段初始化的参数化构造函数。
  • Code 第 11-14 行: 创建一种用于数学运算的方法。
  • Code 线18: 创建另一种方法来打印消息。
  • Code 线20: 创建一个新字符串来打印消息。
  • Code 线22: 打印第 20 行创建的新消息。

让我们为上述内容创建一个测试类。 Java 用于验证异常的类。

下面这个测试类用于对上述代码抛出的异常(此处为 ArithmeticException)进行单元测试。 Java 类:

Airthematic测试.java

下面的屏幕截图显示了编辑器中的同一测试,文件保存为 AirthematicTest1,第 13 行突出显示了预期参数:

AirthematicTest1 类 Eclipse 第 13 行高亮显示了 @Test(expected = ArithmeticException.class)。

package guru99.junit;

import static org.junit.Assert.assertEquals;

import org.junit.Test;

public class AirthematicTest {

	public String message = "Saurabh";
	
	JUnitMessage junitMessage = new JUnitMessage(message);
	
	@Test(expected = ArithmeticException.class)
	public void testJUnitMessage(){

		System.out.println("Junit Message is printing ");
		junitMessage.printMessage();

	}

	@Test
	public void testJUnitHiMessage(){ 
		message="Hi!" + message;
		System.out.println("Junit Message is printing ");
		assertEquals(message, junitMessage.printHiMessage());
	
	}
}

Code 说明:

  • Code 线13: 使用 `@Test` 注解创建测试。执行上述类的方法时,它会调用一个数学运算。这里预期会抛出 `ArithmeticException` 异常,因此将其作为参数添加到 `@Test` 中。
  • Code 线17: 从调用 printMessage() JUnit消息.java。
  • Code 第 21-22 行: 创建另一个测试方法来检查 Hi 消息,这次没有预期的参数。

该类包含两个测试方法,因此一次运行会执行这两个方法:一个是期望返回 ArithmeticException 的方法,另一个是断言返回的字符串的方法。

注意: 这个示例在原始资料中有三个不同的名称——清单文件中的类名为 AirthematicTest,编辑器截图中显示为 AirthematicTest1,而结果视图中则显示为 JunitTestExample。代码完全相同,只有文件名不同。

让我们执行一下并验证结果。 JUnit 下方视图报告了运行情况 Junit测试示例.java.

输出:

以下是输出结果,显示测试成功,没有失败。 trace 如下所示:

Eclipse JUnit 查看报告显示,JunitTestExample 的 2 次运行全部成功,无任何错误或失败。

两种方法都通过了。第一种方法通过是因为它声明的 ArithmeticException 异常确实发生了,第二种方法通过是因为返回的字符串匹配。如果除法运算没有抛出异常, JUnit 第一种方法会失败,并显示消息“预期异常:java.lang.ArithmeticException”。

测试异常的三种方法 JUnit 4

预期参数是三个参数中最短的。 JUnit 有 4 种习语,但并非总是正确的。表格通过两个决定选择的问题对它们进行比较:你能确定信息的含义吗?你知道是哪句话说的吗?

途径 这条信息是否属实? 固定投掷线? 最适合
@Test(expected = X.class) 没有 不——方法中的任何一行都可能抛出异常。 简短的测试,只关注异常类型。
尝试/失败/捕获 是的,在 catch 块内部 是的——只有经过监控的通话才会被监听。 必须检查信息或原因的测试。
@Rule ExpectedException 是的,通过 expectMessage() 没有 传统套件已经基于规则构建。

引言中提到的 fail() 惯用法如下所示。如果调用没有抛出异常,fail() 就会运行,测试会报告你编写的消息:

@Test
public void testDivideByZero() {
    try {
        junitMessage.printMessage();
        fail("Expected an ArithmeticException");
    } catch (ArithmeticException e) {
        assertEquals("/ by zero", e.getMessage());
    }
}

ExpectedException 规则位于两者之间。它已被弃用。 JUnit 4.13 倾向于使用接下来描述的 assertThrows 方法,因此是新的。 JUnit 4项测试不应采用它。

如何测试异常 JUnit 5. 使用 assertThrows()

JUnit 5 完全移除了 @Test 的预期参数。Jupiter 提供 assertThrows()它接受异常类和一个包含待测代码的 lambda 表达式。它会返回捕获到的异常,以便之后可以断言异常消息、异常原因以及任何自定义字段。

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;

public class AirthematicJupiterTest {

    JUnitMessage junitMessage = new JUnitMessage("Saurabh");

    @Test
    public void testJUnitMessage() {
        ArithmeticException thrown = assertThrows(
                ArithmeticException.class,
                () -> junitMessage.printMessage());
        assertEquals("/ by zero", thrown.getMessage());
    }
}

三个相关 断言 完善家庭成员:

  • 断言抛出异常 接受异常类型或其任何子类。
  • assertThrowsExactly 拒绝子类,因此只有指定的类型才能通过。
  • assertDoesNotThrow 这表明预期结果恰恰相反,即该代码块能够干净利落地完成。

此 JUnit 此页面上的 4 代码仍在运行 JUnit 平台通过复古引擎运行,因此在项目迁移过程中,以上所有内容都不需要重写即可继续工作。

测试异常时常见的错误 JUnit

异常测试会以少数几种可识别的方式失败。每一行都列出了症状、原因和解决方法。

症状 原因 固定
预期异常:java.lang.ArithmeticException 该方法已成功完成,未出现任何异常。 确认输入确实无效后,重新运行。
测试通过了,但抛出了错误的行。 预期参数会监视整个方法,包括设置过程。 将设置移出,或者切换到 assertThrows 调用一次。
编辑器中出现未处理的异常类型 抛出了一个已检查异常,但从未声明过该异常。 在测试方法签名中添加 throws 异常。
测试通过了你意料之外的子类 assertThrows 接受指定类型的子类。 使用 assertThrowsExactly 可以实现严格的类型匹配。
expected 不是有效属性 该测试是针对 Jupiter @Test 注解编译的。 导入 org.junit.Test JUnit 4,或者移至 assertThrows。

最后一行最容易让迁移过程中的人出错,因为两个注解都叫做 @Test,只有导入语句才能区分它们。Keeping 一种 JUnit 每版本 测试用例 这类方法避免了整个类型的问题。

常见问题

预期参数无法检查消息。请将调用放在 try 代码块中,紧接着调用 fail(),并在 catch 代码块内对 getMessage() 进行断言。 JUnit 5 简化了这一过程,因为 assertThrows 会返回捕获到的异常。

JUnit 测试失败,并报告“预期异常”,后面跟着你指定的类。除此之外没有其他报告,因此一个从未抛出异常的测试看起来与生产代码悄悄更改了行为的测试完全相同。

AI助手读取方法,列出到达每个throw语句的输入,并为每个分支编写测试。将输出视为起点:助手会从代码中推断异常类型,因此即使抛出错误,也会被忠实地复制到测试中。

副驾驶 遵循文件中已有的样式,因此混合项目会采用混合样式。接受建议前请检查导入行,因为 org.junit.Test 和 org.junit.jupiter.api.Test 在编辑器中看起来完全相同。

它仍然存在,但已被弃用。 JUnit 4.13 版本从未移植到 Jupiter 版本。现有测试套件可以继续使用;新测试应该使用 assertThrows,它更易读,而且不需要公共规则字段。

是的。lambda 参数是一个 Executable 对象,它被声明为抛出 Throwable 异常,因此已检查异常不需要在测试方法本身中添加 throws 子句。同样的情况也适用于…… JUnit 4 尝试并掌握成语。

`assertThrows` 接受任何指定类型的子类,因此预期抛出 `RuntimeException` 也会抛出 `NullPointerException`。当需要精确指定类型时,请使用 `assertThrowsExactly`,或者指定你实际预期的最窄类型。

只有当例外情况是条件的一部分时才成立。tract. 测试异常,该方法永远不会保证在意外行为中抛出锁,并且使重构更加困难,这违背了测试的意义。

总结一下这篇文章: