0

0

JUnit与Mockito:精准测试内部依赖抛出检查型异常的catch块

花韻仙語

花韻仙語

发布时间:2025-10-21 10:46:00

|

437人浏览过

|

来源于php中文网

原创

JUnit与Mockito:精准测试内部依赖抛出检查型异常的catch块

本文深入探讨了在junit测试中如何有效覆盖java服务层方法内`try-catch`块的异常处理路径。通过使用mockito模拟内部依赖抛出检查型异常,我们能够验证服务方法在特定异常场景下的行为,确保异常处理逻辑的健壮性,避免了直接模拟服务方法本身导致的问题。

软件开发中,服务层方法经常需要与外部系统或库进行交互,这些交互过程中可能会抛出各种异常。为了确保应用程序的健壮性,我们通常会使用try-catch块来捕获并处理这些潜在的异常。然而,在编写单元测试时,如何有效地覆盖这些catch块的逻辑,尤其是当异常是由内部依赖抛出时,是一个常见的挑战。

待测试的服务方法示例

考虑以下服务方法,它尝试通过snsProducer发送一条消息,并在发送过程中遇到JsonProcessingException时,将其包装成自定义的SnSException抛出:

public class MyService {

    private SnsProducer snsProducer; // 假设这是一个被注入的依赖

    public MyService(SnsProducer snsProducer) {
        this.snsProducer = snsProducer;
    }

    public void doCreate(Message message) {
        try {
            snsProducer.send(message);
        } catch (JsonProcessingException jpe) {
            // 将JsonProcessingException包装成自定义的SnSException抛出
            throw new SnSException("Could not parse Message to publish to SNS", jpe);
        }
    }
}

// 假设SnSException是一个自定义的运行时异常
class SnSException extends RuntimeException {
    public SnSException(String message, Throwable cause) {
        super(message, cause);
    }
}

// 假设SnsProducer接口及其send方法
interface SnsProducer {
    void send(Message message) throws JsonProcessingException;
}

// 假设Message是一个简单的POJO
class Message {}

我们的目标是编写一个JUnit测试,来验证当snsProducer.send(message)抛出JsonProcessingException时,doCreate方法确实会捕获它并抛出SnSException。

错误的测试尝试及其原因

在尝试覆盖catch块时,开发者可能会遇到一些常见的误区。

误区一:直接模拟服务方法本身抛出检查型异常

@Test
void snsTest_IncorrectAttempt1() {
    // 假设service和message已被正确初始化
    // MyService service = new MyService(mock(SnsProducer.class));
    // Message message = new Message();

    // 尝试让service.doCreate(message)直接抛出JsonProcessingException
    // 这里的service是真实的实例,不是mock对象
    // 如果service是mock对象,且doCreate方法没有声明抛出JsonProcessingException,
    // 则会编译错误或运行时异常
    // when(service.doCreate(message)).thenThrow(new JsonProcessingException("Json Processing Error"){}); // 编译错误或运行时异常
    // assertThrows(SnSException.class, () -> service.doCreate(message));
}

问题分析: 如果service是一个真实的MyService实例,when().thenThrow()不能直接作用于它。如果service是一个Mockito的mock对象,那么doCreate方法本身并没有声明会抛出JsonProcessingException(它内部处理了),所以尝试让mock对象直接抛出JsonProcessingException会导致Checked exception is invalid for this method!这样的错误。这是因为Mockito在模拟方法抛出检查型异常时,会检查被模拟方法的签名是否声明了该异常。

误区二:直接模拟服务方法本身抛出预期的运行时异常

@Test
void snsTest_IncorrectAttempt2() {
    // 假设service和message已被正确初始化
    // MyService service = new MyService(mock(SnsProducer.class));
    // Message message = new Message();

    // 尝试让service.doCreate(message)直接抛出SnSException
    // when(service.doCreate(message)).thenThrow(new SnSException("Exception", new JsonProcessingException(""))); // 假设service是mock对象
    // assertThrows(SnSException.class, () -> service.doCreate(message));
}

问题分析: 这种方法虽然可以避免编译错误(因为SnSException是运行时异常),但它完全绕过了MyService中doCreate方法的实际逻辑。when(service.doCreate(message)).thenThrow(...)意味着当调用service.doCreate(message)时,直接抛出SnSException,而不会执行try-catch块内部的snsProducer.send(message)调用。因此,我们并没有真正测试到catch块的逻辑,而是测试了mock对象的行为。assertThrows会捕获到我们模拟的SnSException,但这不是我们想要测试的场景。

CodeBuddy
CodeBuddy

腾讯云AI代码助手

下载

正确的测试方法:模拟内部依赖的行为

要正确测试catch块,关键在于让try块内部的实际代码路径抛出异常,从而触发catch块的执行。这意味着我们需要模拟MyService的内部依赖snsProducer的行为。

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import com.fasterxml.jackson.core.JsonProcessingException;

import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.when;

public class MyServiceTest {

    @Mock
    private SnsProducer snsProducer; // 模拟SnsProducer依赖

    @InjectMocks
    private MyService myService; // 注入mock的snsProducer到myService中

    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this); // 初始化mock对象
    }

    @Test
    void doCreate_shouldThrowSnSException_whenJsonProcessingExceptionOccurs() throws JsonProcessingException {
        Message testMessage = new Message(); // 创建一个测试消息对象

        // 核心:当snsProducer.send(any(Message.class))被调用时,抛出JsonProcessingException
        // 这里使用doThrow().when(),或者when().thenThrow()都可以,取决于个人偏好和方法签名
        // 因为send方法声明了throws JsonProcessingException,所以可以直接使用when().thenThrow()
        when(snsProducer.send(any(Message.class)))
            .thenThrow(new JsonProcessingException("模拟的JSON处理错误") {});

        // 验证当调用myService.doCreate(testMessage)时,是否抛出了SnSException
        assertThrows(SnSException.class, () -> myService.doCreate(testMessage));
    }
}

解释:

  1. @Mock private SnsProducer snsProducer;: 我们创建了一个SnsProducer的模拟对象。
  2. @InjectMocks private MyService myService;: Mockito会将@Mock标记的snsProducer自动注入到myService实例中。
  3. when(snsProducer.send(any(Message.class))).thenThrow(new JsonProcessingException("模拟的JSON处理错误") {});: 这是关键一步。我们指示Mockito:当myService内部调用其snsProducer的send方法时(无论传入什么Message对象,因为使用了any(Message.class)),都不要执行实际的send逻辑,而是直接抛出一个JsonProcessingException实例。
  4. assertThrows(SnSException.class, () -> myService.doCreate(testMessage));: 最后,我们断言当调用myService.doCreate(testMessage)时,会抛出SnSException。

通过这种方式,myService.doCreate(testMessage)方法会正常执行到try块内部的snsProducer.send(message)。由于snsProducer已经被模拟成抛出JsonProcessingException,这个异常会被doCreate方法中的catch (JsonProcessingException jpe)块捕获,进而抛出SnSException。assertThrows成功捕获到这个SnSException,从而验证了catch块的逻辑是正确的。

注意事项与最佳实践

  • 关注点分离: 单元测试应该专注于测试单个组件(这里是MyService)的逻辑,而不是其依赖项的逻辑。通过模拟依赖项,我们可以隔离测试范围。
  • 模拟正确的对象: 异常应该由try块内部实际调用的方法抛出。因此,应该模拟该方法所属的依赖对象,而不是被测试的服务方法本身。
  • 匹配异常类型: 确保你模拟抛出的异常类型与catch块声明捕获的异常类型相匹配。如果catch块捕获的是IOException,而你模拟抛出NullPointerException,那么catch块将不会被触发。
  • 验证异常内容: 除了验证异常类型,还可以进一步验证异常消息或嵌套异常(cause),以确保异常被正确包装和传递。
  • 避免过度模拟: 只模拟那些必要的部分。如果一个方法内部的某个依赖在当前测试场景中不会抛出异常,或者其返回值不影响测试结果,则无需对其进行复杂的模拟。

总结

在JUnit测试中覆盖try-catch块,特别是当异常来源于内部依赖时,需要精确地模拟依赖的行为。通过使用Mockito的when().thenThrow()(或doThrow().when())机制,我们可以让被测试方法内部的依赖在特定条件下抛出预期的异常,从而激活catch块的逻辑,验证其处理路径的正确性。这种方法确保了测试的准确性和隔离性,是编写健壮单元测试的关键实践之一。

热门AI工具

更多
DeepSeek
DeepSeek

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

豆包大模型
豆包大模型

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

通义千问
通义千问

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

腾讯元宝
腾讯元宝

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

文心一言
文心一言

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

讯飞写作
讯飞写作

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

即梦AI
即梦AI

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

ChatGPT
ChatGPT

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

相关专题

更多
json数据格式
json数据格式

JSON是一种轻量级的数据交换格式。本专题为大家带来json数据格式相关文章,帮助大家解决问题。

419

2023.08.07

json是什么
json是什么

JSON是一种轻量级的数据交换格式,具有简洁、易读、跨平台和语言的特点,JSON数据是通过键值对的方式进行组织,其中键是字符串,值可以是字符串、数值、布尔值、数组、对象或者null,在Web开发、数据交换和配置文件等方面得到广泛应用。本专题为大家提供json相关的文章、下载、课程内容,供大家免费下载体验。

535

2023.08.23

jquery怎么操作json
jquery怎么操作json

操作的方法有:1、“$.parseJSON(jsonString)”2、“$.getJSON(url, data, success)”;3、“$.each(obj, callback)”;4、“$.ajax()”。更多jquery怎么操作json的详细内容,可以访问本专题下面的文章。

311

2023.10.13

go语言处理json数据方法
go语言处理json数据方法

本专题整合了go语言中处理json数据方法,阅读专题下面的文章了解更多详细内容。

77

2025.09.10

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

软件测试常用工具有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

class在c语言中的意思
class在c语言中的意思

在C语言中,"class" 是一个关键字,用于定义一个类。想了解更多class的相关内容,可以阅读本专题下面的文章。

469

2024.01.03

clawdbot ai使用教程 保姆级clawdbot部署安装手册
clawdbot ai使用教程 保姆级clawdbot部署安装手册

Clawdbot是一个“有灵魂”的AI助手,可以帮用户清空收件箱、发送电子邮件、管理日历、办理航班值机等等,并且可以接入用户常用的任何聊天APP,所有的操作均可通过WhatsApp、Telegram等平台完成,用户只需通过对话,就能操控设备自动执行各类任务。

14

2026.01.29

热门下载

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

精品课程

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

共23课时 | 3万人学习

C# 教程
C# 教程

共94课时 | 7.9万人学习

Java 教程
Java 教程

共578课时 | 52.9万人学习

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

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