《Java单元测试实战》——案例集锦:Java单元测试典型案例集锦(8)

简介: 《Java单元测试实战》——案例集锦:Java单元测试典型案例集锦(8)

《Java单元测试实战》——案例集锦:Java单元测试典型案例集锦(7) https://developer.aliyun.com/article/1232052?groupCode=java



八、 如何测试已变更的方法参数值

 

在单元测试中,我们通常通过ArgumentCaptor进行方法参数捕获并验证。但是,在有些情况下,我们捕获的可能是已经变更的方法参数,所以无法对这些方法参数值进行验证。

 

1. 案例代码

 

这里,以分批读取并保存ODPS数据为例说明。其中,dataList在每次存储后,都进行了一次清除操作。


/**
 * 读取数据
 *
 * @param <T> 模板类型
 * @param recordReader 记录读取器
 * @param batchSize 批量大小
 * @param dataParser 数据解析器
 * @param dataStorage 数据存储器
 * @throws IOException IO异常
 */
public static <T> void readData(RecordReader recordReader, int batchSize,
    Function<Record, T> dataParser, Consumer<List<T>> dataStorage) throws IOException {
    // 依次读取数据
    Record record;
    List<T> dataList = new ArrayList<>(batchSize);
    while (Objects.nonNull(record = recordReader.read())) {
        // 解析添加数据
        T data = dataParser.apply(record);
        if (Objects.nonNull(data)) {
            dataList.add(data);
        }
        // 批量存储数据
        if (dataList.size() == batchSize) {
            dataStorage.accept(dataList);
            dataList.clear();
        }
    }
    // 存储剩余数据
    if (CollectionUtils.isNotEmpty(dataList)) {
        dataStorage.accept(dataList);
        dataList.clear();
    }
}

2. 问题测试

 

通常情况下,我们利用ArgumentCaptor编写的测试用例如下:


/**
 * 测试: 读取数据-正常
 * 
 * @throws IOException IO异常
 */
@Test
public void testReadDataWithNormal() throws IOException {
    // 模拟依赖方法
    // 模拟依赖方法: recordReader.read
    Record record1 = Mockito.mock(Record.class);
    Record record2 = Mockito.mock(Record.class);
    TunnelRecordReader recordReader = Mockito.mock(TunnelRecordReader.class);
    Mockito.doReturn(record1, record2, null).when(recordReader).read();
    // 模拟依赖方法: dataParser.apply
    Function<Record, Object> dataParser = CastUtils.cast(Mockito.mock(Function.class));
    Object object1 = new Object();
    Object object2 = new Object();
    Mockito.doReturn(object1).when(dataParser).apply(record1);
    Mockito.doReturn(object2).when(dataParser).apply(record2);
    // 调用测试方法
    int batchSize = 2;
    Consumer<List<Object>> dataStorage = CastUtils.cast(Mockito.mock(Consumer.class));
    OdpsHelper.readData(recordReader, batchSize, dataParser, dataStorage);
    // 验证依赖方法
    // 验证依赖方法: recordReader.read
    Mockito.verify(recordReader, Mockito.times(3)).read();
    // 验证依赖方法: dataParser.apply
    Mockito.verify(dataParser).apply(record1);
    Mockito.verify(dataParser).apply(record2);
    // 验证依赖方法: dataStorage.test
    ArgumentCaptor<List<Object>> dataListCaptor = CastUtils.cast(ArgumentCaptor.forClass(List.class));
    Mockito.verify(dataStorage).accept(dataListCaptor.capture());
    Assert.assertEquals("数据列表不一致", Arrays.asList(object1, object2), dataListCaptor.getValue());
}

 执行该单元测试后,会出现以下错误:

image.png

因为,我们捕获的方法参数dataList只是一个对象引用,其数据内容早已被clear方法清除干净了。

 

3. 正确测试

 

对于这种情况,我们可以利用Mockito.doAnswer来保存这些临时值,最后再进行统一的数据验证。


/**
 * 测试: 读取数据-正常
 * 
 * @throws IOException IO异常
 */
@Test
public void testReadDataWithNormal() throws IOException {
    // 模拟依赖方法
    // 模拟依赖方法: recordReader.read
    Record record1 = Mockito.mock(Record.class);
    Record record2 = Mockito.mock(Record.class);
    TunnelRecordReader recordReader = Mockito.mock(TunnelRecordReader.class);
    Mockito.doReturn(record1, record2, null).when(recordReader).read();
    // 模拟依赖方法: dataParser.apply
    Function<Record, Object> dataParser = CastUtils.cast(Mockito.mock(Function.class));
    Object object1 = new Object();
    Object object2 = new Object();
    Mockito.doReturn(object1).when(dataParser).apply(record1);
    Mockito.doReturn(object2).when(dataParser).apply(record2);
    // 模拟依赖方法: dataStorage.test
    List<Object> dataList = new ArrayList<>();
    Consumer<List<Object>> dataStorage = CastUtils.cast(Mockito.mock(Consumer.class));
    Mockito.doAnswer(invocation -> dataList.addAll(invocation.getArgument(0)))
        .when(dataStorage).accept(Mockito.anyList());
    // 调用测试方法
    int batchSize = 2;
    OdpsHelper.readData(recordReader, batchSize, dataParser, dataStorage);
    Assert.assertEquals("数据列表不一致", Arrays.asList(object1, object2), dataList);
    // 验证依赖方法
    // 验证依赖方法: recordReader.read
    Mockito.verify(recordReader, Mockito.times(3)).read();
    // 验证依赖方法: dataParser.apply
    Mockito.verify(dataParser).apply(record1);
    Mockito.verify(dataParser).apply(record2);
    // 验证依赖方法: dataStorage.test
    Mockito.verify(dataStorage).accept(Mockito.anyList());
}



《Java单元测试实战》——案例集锦:Java单元测试典型案例集锦(9) https://developer.aliyun.com/article/1232050?groupCode=java

相关文章
|
12月前
|
安全 Java API
Java 集合高级应用与实战技巧之高效运用方法及实战案例解析
本课程深入讲解Java集合的高级应用与实战技巧,涵盖Stream API、并行处理、Optional类、现代化Map操作、不可变集合、异步处理及高级排序等核心内容,结合丰富示例,助你掌握Java集合的高效运用,提升代码质量与开发效率。
441 0
|
12月前
|
安全 JavaScript Java
java Web 项目完整案例实操指南包含从搭建到部署的详细步骤及热门长尾关键词解析的实操指南
本项目为一个完整的JavaWeb应用案例,采用Spring Boot 3、Vue 3、MySQL、Redis等最新技术栈,涵盖前后端分离架构设计、RESTful API开发、JWT安全认证、Docker容器化部署等内容,适合掌握企业级Web项目全流程开发与部署。
1038 0
|
缓存 算法 NoSQL
校招 Java 面试高频常见知识点深度解析与实战案例详细分享
《2025校招Java面试核心指南》总结了Java技术栈的最新考点,涵盖基础语法、并发编程和云原生技术三大维度: 现代Java特性:重点解析Java 17密封类、Record类型及响应式Stream API,通过电商案例演示函数式数据处理 并发革命:对比传统线程池与Java 21虚拟线程,详解Reactor模式在秒杀系统中的应用及背压机制 云原生实践:提供Spring Boot容器化部署方案,分析Spring WebFlux响应式编程和Redis Cluster缓存策略。
389 0
|
人工智能 Java API
Java 生态大模型应用开发全流程实战案例与技术路径终极对决
在Java生态中开发大模型应用,Spring AI、LangChain4j和JBoltAI是三大主流框架。本文从架构设计、核心功能、开发体验、性能扩展性、生态社区等维度对比三者特点,并结合实例分析选型建议。Spring AI适合已有Spring技术栈团队,LangChain4j灵活性强适用于学术研究,JBoltAI提供开箱即用的企业级解决方案,助力传统系统快速AI化改造。开发者可根据业务场景和技术背景选择最适合的框架。
2764 2
|
自然语言处理 前端开发 Java
JBoltAI 框架完整实操案例 在 Java 生态中快速构建大模型应用全流程实战指南
本案例基于JBoltAI框架,展示如何快速构建Java生态中的大模型应用——智能客服系统。系统面向电商平台,具备自动回答常见问题、意图识别、多轮对话理解及复杂问题转接人工等功能。采用Spring Boot+JBoltAI架构,集成向量数据库与大模型(如文心一言或通义千问)。内容涵盖需求分析、环境搭建、代码实现(知识库管理、核心服务、REST API)、前端界面开发及部署测试全流程,助你高效掌握大模型应用开发。
1143 5
|
缓存 NoSQL Java
Java Redis 面试题集锦 常见高频面试题目及解析
本文总结了Redis在Java中的核心面试题,包括数据类型操作、单线程高性能原理、键过期策略及分布式锁实现等关键内容。通过Jedis代码示例展示了String、List等数据类型的操作方法,讲解了惰性删除和定期删除相结合的过期策略,并提供了Spring Boot配置Redis过期时间的方案。文章还探讨了缓存穿透、雪崩等问题解决方案,以及基于Redis的分布式锁实现,帮助开发者全面掌握Redis在Java应用中的实践要点。
711 6
|
缓存 Java API
Java 集合容器实操技巧与案例详解
本教程基于Java 8+新特性和现代开发实践,深入讲解Java集合容器的实操技巧。通过具体场景演示Stream API数据处理、ConcurrentHashMap并发控制、LinkedHashMap实现LRU缓存、TreeSet自定义排序等高级特性。同时涵盖computeIfAbsent优化操作、EnumMap专用集合使用、集合统计与运算(交集、并集、差集)等内容。代码示例丰富,助力掌握高效编程方法。[点击获取完整代码](https://pan.quark.cn/s/14fcf913bae6)。
348 0
|
前端开发 JavaScript Java
Java 学习路线规划及项目案例中的技术栈应用解析
内容包括:**Java 17核心特性**(如sealed class、record)与模块化开发;Spring Boot 3 + Spring Cloud微服务架构,涉及响应式编程(WebFlux)、多数据库持久化(JPA、R2DBC、MongoDB);云原生技术**如Docker、Kubernetes及CI/CD流程;性能优化(GraalVM Native Image、JVM调优);以及前后端分离开发(Vue 3、Spring Boot集成)。通过全栈电商平台项目实战,掌握从后端服务(用户、商品、订单)到前端应用(Vue 3、React Native)的全流程开发。
616 9
|
存储 Java 数据安全/隐私保护
Java技术栈揭秘:Base64加密和解密文件的实战案例
以上就是我们今天关于Java实现Base64编码和解码的实战案例介绍。希望能对你有所帮助。还有更多知识等待你去探索和学习,让我们一同努力,继续前行!
715 5
|
缓存 NoSQL Java
校招 Java 面试常见知识点及实战案例全解析
本文全面解析了Java校招面试中的常见知识点,涵盖Java新特性(如Lambda表达式、、Optional类)、集合框架高级应用(线程安全集合、Map性能优化)、多线程与并发编程(线程池配置)、JVM性能调优(内存溢出排查、垃圾回收器选择)、Spring与微服务实战(Spring Boot自动配置)、数据库与ORM框架(MyBatis高级用法、索引优化)、分布式系统(分布式事务、缓存应用)、性能优化(接口优化、高并发限流)、单元测试与代码质量(JUnit 5、Mockito、JaCoCo)以及项目实战案例(电商秒杀系统、社交消息推送)。资源地址: [https://pan.quark.cn/s
339 4