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表达式),这段代码在执行时应该抛出预期的异常。

奇布塔
奇布塔

基于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断言,我们可以编写出既健壮又富有表达力的异常测试用例。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
软件测试常用工具
软件测试常用工具

软件测试常用工具有Selenium、JUnit、Appium、JMeter、LoadRunner、Postman、TestNG、LoadUI、SoapUI、Cucumber和Robot Framework等等。测试人员可以根据具体的测试需求和技术栈选择适合的工具,提高测试效率和准确性 。

439

2023.10.13

java测试工具有哪些
java测试工具有哪些

java测试工具有JUnit、TestNG、Mockito、Selenium、Apache JMeter和Cucumber。php还给大家带来了java有关的教程,欢迎大家前来学习阅读,希望对大家能有所帮助。

300

2023.10.23

Java 单元测试
Java 单元测试

本专题聚焦 Java 在软件测试与持续集成流程中的实战应用,系统讲解 JUnit 单元测试框架、Mock 数据、集成测试、代码覆盖率分析、Maven 测试配置、CI/CD 流水线搭建(Jenkins、GitHub Actions)等关键内容。通过实战案例(如企业级项目自动化测试、持续交付流程搭建),帮助学习者掌握 Java 项目质量保障与自动化交付的完整体系。

19

2025.10.24

lambda表达式
lambda表达式

Lambda表达式是一种匿名函数的简洁表示方式,它可以在需要函数作为参数的地方使用,并提供了一种更简洁、更灵活的编码方式,其语法为“lambda 参数列表: 表达式”,参数列表是函数的参数,可以包含一个或多个参数,用逗号分隔,表达式是函数的执行体,用于定义函数的具体操作。本专题为大家提供lambda表达式相关的文章、下载、课程内容,供大家免费下载体验。

207

2023.09.15

python lambda函数
python lambda函数

本专题整合了python lambda函数用法详解,阅读专题下面的文章了解更多详细内容。

191

2025.11.08

Python lambda详解
Python lambda详解

本专题整合了Python lambda函数相关教程,阅读下面的文章了解更多详细内容。

53

2026.01.05

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

1100

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

189

2025.10.17

php中文乱码如何解决
php中文乱码如何解决

本文整理了php中文乱码如何解决及解决方法,阅读节专题下面的文章了解更多详细内容。

1

2026.01.28

热门下载

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

精品课程

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

共23课时 | 2.9万人学习

C# 教程
C# 教程

共94课时 | 7.8万人学习

Java 教程
Java 教程

共578课时 | 52.3万人学习

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

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