0

0

AssertJ异常处理:正确测试预期异常的方法

花韻仙語

花韻仙語

发布时间:2025-09-17 11:16:25

|

554人浏览过

|

来源于php中文网

原创

AssertJ异常处理:正确测试预期异常的方法

本文探讨了在使用AssertJ进行单元测试时,如何正确地验证方法抛出的预期异常。针对常见的assertThat().isInstanceOf()误用,文章详细介绍了AssertJ提供的assertThrows方法,并通过示例代码演示了其正确用法,确保测试能够准确捕获和验证异常类型,从而提升测试的健壮性和准确性。

软件开发中,单元测试是确保代码质量的关键环节。当我们的方法在特定条件下预期会抛出异常时,如何有效地测试这些异常行为成为了一个重要课题。assertj作为一个功能强大的断言库,提供了多种机制来处理这种情况。

1. 常见误区:直接断言方法返回值

许多开发者在初次尝试测试异常时,可能会直观地尝试对方法调用的结果进行断言,以检查是否抛出了特定类型的异常。以下是一个常见的错误示例:

假设我们有一个方法enterTheAmount,它接受用户输入的金额,并检查该金额是否为指定价格的倍数。如果不是,则抛出IllegalArgumentException。

public class Application {
    public static int enterTheAmount(){
        final int LOTTO_PRICE = 1000;
        // 模拟从控制台读取输入,这里简化为直接解析字符串
        // 在实际测试中,通常会通过System.setIn()重定向输入流
        int amount = Integer.parseInt(Console.readLine()); // 假设Console.readLine()已实现
        if(amount % LOTTO_PRICE != 0) {
            throw new IllegalArgumentException("金额必须是" + LOTTO_PRICE + "的倍数。");
        }
        return amount / LOTTO_PRICE;
    }
}

为了测试当输入无效金额时是否抛出IllegalArgumentException,可能会有如下错误的测试尝试:

import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.ByteArrayInputStream;
import java.io.InputStream;

class ApplicationTest {

    // 模拟Console.readLine()所需的输入流重定向
    private void setSystemIn(String input) {
        InputStream in = new ByteArrayInputStream(input.getBytes());
        System.setIn(in);
    }

    @Test
    void validateTheEnteredAmount_incorrectly() {
        final String INVALID_NUMBER = "1234";
        setSystemIn(INVALID_NUMBER); // 设置模拟输入

        // 错误的断言方式:试图对方法的返回值进行isInstanceOf判断
        // 这段代码会失败,因为异常会在assertThat()被调用之前抛出
        // 导致测试框架捕获到一个未处理的异常,而不是AssertJ的断言失败
        assertThat(Application.enterTheAmount())
                .isInstanceOf(IllegalArgumentException.class);
    }
}

问题分析:

上述测试代码的根本问题在于,Application.enterTheAmount()方法在遇到无效输入(如"1234")时会立即抛出IllegalArgumentException。这意味着assertThat()方法根本无法接收到任何返回值来执行isInstanceOf()断言。异常会在方法调用时直接中断程序的正常流程,并被JUnit测试框架捕获为未处理的异常,从而导致测试失败,而不是我们期望的AssertJ断言失败。

2. 正确姿势:使用 assertThrows 验证异常

AssertJ以及JUnit 5(通过assertThrows)提供了专门用于测试异常的方法,能够优雅且准确地验证预期异常。AssertJ的assertThrows方法(实际上是Assertions.assertThatThrownBy或JUnit 5的assertThrows)是解决此类问题的正确途径。这里我们主要介绍JUnit 5的assertThrows,因为它在现代Java测试中更为常用且语义清晰。

assertThrows方法接受两个参数:预期的异常类型和一段可执行的代码(通常是一个lambda表达式),这段代码在执行时应该抛出预期的异常。

Stable Video
Stable Video

Stability AI 发布的开源AI视频大模型,用文字或图像创建视频,把你的概念变成迷人的电影

下载

以下是使用assertThrows改进后的正确测试代码:

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertThrows; // 导入JUnit 5的assertThrows
import java.io.ByteArrayInputStream;
import java.io.InputStream;

class ApplicationTest {

    // 模拟Console.readLine()所需的输入流重定向
    private void setSystemIn(String input) {
        InputStream in = new ByteArrayInputStream(input.getBytes());
        System.setIn(in);
    }

    @Test
    void validateTheEnteredAmount_correctly() {
        final String INVALID_NUMBER = "1234";
        setSystemIn(INVALID_NUMBER); // 设置模拟输入

        // 正确的断言方式:使用assertThrows验证异常
        assertThrows(IllegalArgumentException.class, () -> Application.enterTheAmount());
    }
}

在这个修正后的测试中:

  1. assertThrows(IllegalArgumentException.class, ...)明确表示我们期望IllegalArgumentException被抛出。
  2. 第二个参数是一个lambda表达式() -> Application.enterTheAmount(),它封装了可能抛出异常的代码。
  3. 当Application.enterTheAmount()执行并抛出IllegalArgumentException时,assertThrows会捕获这个异常,并验证其类型是否与预期相符。如果相符,测试通过;如果不符或没有抛出异常,测试失败。

3. assertThrows 方法深度解析

assertThrows是JUnit 5提供的一个强大工具,用于验证方法是否抛出了特定的异常。

  • 方法签名: assertThrows(Class expectedType, Executable executable)
    • expectedType: 期望被抛出的异常的Class对象。
    • executable: 一个Executable接口的实例,通常以lambda表达式的形式提供,包含可能抛出异常的代码。
  • 返回结果: assertThrows方法会返回被捕获到的异常对象。这意味着我们可以进一步对这个异常对象进行断言,例如检查异常消息。

示例:进一步断言异常信息

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.assertj.core.api.Assertions.assertThat; // 结合AssertJ进行更丰富的断言
import java.io.ByteArrayInputStream;
import java.io.InputStream;

class ApplicationTest {

    private void setSystemIn(String input) {
        InputStream in = new ByteArrayInputStream(input.getBytes());
        System.setIn(in);
    }

    @Test
    void validateTheEnteredAmount_withExceptionMessageCheck() {
        final String INVALID_NUMBER = "1234";
        setSystemIn(INVALID_NUMBER);

        // 捕获异常对象并进行进一步断言
        IllegalArgumentException thrown = assertThrows(
                IllegalArgumentException.class,
                () -> Application.enterTheAmount(),
                "当输入无效金额时,应该抛出IllegalArgumentException" // 失败时的可选消息
        );

        // 使用AssertJ断言异常消息
        assertThat(thrown.getMessage())
                .isNotNull()
                .contains("金额必须是")
                .contains("1000的倍数");
    }
}

在这个例子中,我们不仅验证了异常类型,还通过返回的thrown对象,使用AssertJ的assertThat对异常消息进行了更详细的断言,确保异常信息符合预期,这大大增加了测试的健壮性。

4. 注意事项与最佳实践

  • 使用Lambda表达式: assertThrows的第二个参数必须是一个Executable或ThrowingCallable(AssertJ中),这意味着你需要将可能抛出异常的代码封装在一个lambda表达式或方法引用中。直接调用方法会导致异常在assertThrows执行之前就被抛出。
  • 具体的异常类型: 尽可能断言最具体的异常类型。例如,如果方法抛出IllegalArgumentException,就断言IllegalArgumentException.class,而不是更泛化的RuntimeException.class或Exception.class。
  • 测试范围: 确保你的测试只验证了预期的异常情况,而不是由于其他原因导致的意外异常。在示例中,我们通过setSystemIn精确控制了输入,以隔离测试条件。
  • 与AssertJ的集成: 尽管assertThrows是JUnit 5的一部分,但它与AssertJ可以很好地结合使用。assertThrows用于捕获和验证异常类型,而AssertJ的assertThat可以用于对捕获到的异常对象(例如异常消息、原因等)进行更丰富的链式断言。

总结

在单元测试中验证预期异常是确保代码行为正确性的重要一环。避免直接对可能抛出异常的方法返回值进行assertThat().isInstanceOf()断言的常见误区。相反,应采用JUnit 5提供的assertThrows方法,它能够清晰、准确地捕获并验证特定类型的异常。通过将可能抛出异常的代码封装在lambda表达式中,并利用assertThrows返回的异常对象进行进一步的AssertJ断言,我们可以编写出既健壮又富有表达力的异常测试用例。

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

826

2023.06.15

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

727

2023.07.05

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

732

2023.07.31

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

396

2023.08.01

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

398

2023.08.02

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

446

2023.08.02

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

429

2023.08.02

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

16904

2023.08.03

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

194

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 2.2万人学习

C# 教程
C# 教程

共94课时 | 5.9万人学习

Java 教程
Java 教程

共578课时 | 41.4万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号