用 Gemini 3.5-flash 辅助 Java 接口测试:从需求描述到可落地测试用例
文章摘要:本文以 Java 后端“订单售后状态更新接口”为例,记录如何用 Gemini 3.5-flash 辅助生成接口测试用例。内容包括需求结构化、Prompt 示例、测试清单整理、JUnit/MockMvc 测试骨架、状态流转校验、边界场景补充、多模型对比和人工验证方法。核心观点是:AI 适合整理规则、补充遗漏和生成初稿,但最终仍需结合业务需求、代码实现、数据库状态、日志与消息结果进行人工 Review 和测试验证。
在后端开发里,接口写完不难,难的是把边界条件、异常路径和回归用例补齐。尤其是一些业务接口,比如订单状态变更、退款回调、库存扣减、优惠券核销,代码看起来只是几个if else,但测试场景一旦漏掉,就容易在联调或上线后暴露问题。
这篇文章记录一次比较具体的实践:使用 Gemini 3.5-flash 辅助整理 Java 后端接口的测试用例。场景选得比较常见:电商系统里的“订单售后状态更新接口”。重点不是让 AI 替代测试或开发判断,而是让它帮助我们把需求、接口参数、业务规则和异常路径整理成可执行的测试清单。
如果只是想低门槛比较多个模型在同一个任务下的输出,也可以了解KULAAI(https://ouai.me)这类多模型聚合工具。它支持 Gemini、ChatGPT、Claude、DeepSeek 等主流模型切换,适合用于模型能力对比、Prompt 调试和日常开发辅助验证。但工具本身不是重点,重点还是建立自己的输入规范、人工 Review 和测试验证流程。
场景背景:一个订单售后状态更新接口
假设我们有一个订单售后模块,前端或内部系统会调用接口更新售后单状态。接口大致如下:
http
POST /api/after-sale/status/update Content-Type: application/json请求参数:
json
{ "afterSaleId": 10001, "orderId": 90001, "status": "APPROVED", "operatorId": 2001, "remark": "审核通过" }业务规则简化如下:
- 售后单必须存在;
- 订单必须存在;
- 售后单必须属于该订单;
- 状态只能按固定流程流转;
- 已关闭或已完成的售后单不能再次修改;
- 操作人不能为空;
- 更新成功后需要记录操作日志;
- 部分状态变更需要触发消息通知。
如果直接让 AI “帮我写测试用例”,输出往往会比较泛。更好的做法是把接口、字段、状态流转和限制条件都提供出来,让模型按指定格式输出。
第一步:把需求转成结构化输入
我给 Gemini 3.5-flash 的 Prompt 是:
你是一名 Java 后端测试开发工程师,请根据下面的接口信息生成测试用例。 接口: POST /api/after-sale/status/update 请求字段: - afterSaleId: Long,售后单 ID,必填 - orderId: Long,订单 ID,必填 - status: String,目标状态,必填 - operatorId: Long,操作人 ID,必填 - remark: String,备注,非必填,最长 200 字 状态枚举: - APPLYING:申请中 - APPROVED:审核通过 - REJECTED:审核拒绝 - RETURNING:退货中 - REFUNDED:已退款 - CLOSED:已关闭 流转规则: - APPLYING 可以变更为 APPROVED、REJECTED、CLOSED - APPROVED 可以变更为 RETURNING、REFUNDED、CLOSED - RETURNING 可以变更为 REFUNDED、CLOSED - REJECTED、REFUNDED、CLOSED 为终态,不允许再变更 业务规则: 1. 售后单必须存在; 2. 订单必须存在; 3. 售后单必须属于该订单; 4. 操作人不能为空; 5. 更新成功后写入操作日志; 6. REFUNDED 状态需要发送退款完成消息。 请输出: - 正常用例; - 参数校验用例; - 状态流转用例; - 数据一致性用例; - 日志和消息验证用例; - 每条用例包含:用例编号、前置条件、请求参数、预期结果。这个 Prompt 的关键点是:不要只描述“我要测接口”,而是把枚举、状态机、业务规则、输出格式都写清楚。Gemini 3.5-flash 在这类结构化整理任务上比较适合,输出通常能直接作为测试用例初稿。
第二步:把输出整理成可执行测试清单
模型输出后,我通常不会直接照搬,而是先整理成表格。示例:
| 用例编号 | 分类 | 前置条件 | 请求状态 | 预期结果 |
|---|---|---|---|---|
| TC001 | 正常流程 | 售后单状态为 APPLYING | APPROVED | 更新成功,状态变为 APPROVED |
| TC002 | 正常流程 | 售后单状态为 APPLYING | REJECTED | 更新成功,状态变为 REJECTED |
| TC003 | 正常流程 | 售后单状态为 APPROVED | RETURNING | 更新成功,状态变为 RETURNING |
| TC004 | 终态限制 | 售后单状态为 REFUNDED | CLOSED | 更新失败,提示终态不可修改 |
| TC005 | 非法流转 | 售后单状态为 APPLYING | REFUNDED | 更新失败,提示状态流转非法 |
| TC006 | 参数校验 | afterSaleId 为空 | APPROVED | 返回参数错误 |
| TC007 | 数据一致性 | 售后单不属于 orderId | APPROVED | 更新失败 |
| TC008 | 消息验证 | 状态变更为 REFUNDED | REFUNDED | 更新成功并发送退款完成消息 |
这一步很适合 AI 辅助,但必须人工检查。比如“APPLYING 直接到 REFUNDED”是否非法,要看真实业务;“APPROVED 到 REFUNDED”是否允许,也要和产品、测试确认。
第三步:用 JUnit 写接口测试骨架
假设项目使用 Spring Boot、JUnit 5 和 MockMvc,可以先让模型生成测试骨架,再由开发补充具体 Mock 和断言。
示例代码:
@SpringBootTest @AutoConfigureMockMvc class AfterSaleStatusControllerTest { @Autowired private MockMvc mockMvc; @MockBean private AfterSaleService afterSaleService; @Test void should_update_status_when_flow_is_valid() throws Exception { AfterSaleStatusUpdateRequest request = new AfterSaleStatusUpdateRequest(); request.setAfterSaleId(10001L); request.setOrderId(90001L); request.setStatus("APPROVED"); request.setOperatorId(2001L); request.setRemark("审核通过"); Mockito.when(afterSaleService.updateStatus(Mockito.any())) .thenReturn(true); mockMvc.perform(MockMvcRequestBuilders.post("/api/after-sale/status/update") .contentType(MediaType.APPLICATION_JSON) .content(toJson(request))) .andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(MockMvcResultMatchers.jsonPath("$.code").value(0)); } @Test void should_return_error_when_after_sale_id_is_null() throws Exception { String body = """ { "orderId": 90001, "status": "APPROVED", "operatorId": 2001, "remark": "审核通过" } """; mockMvc.perform(MockMvcRequestBuilders.post("/api/after-sale/status/update") .contentType(MediaType.APPLICATION_JSON) .content(body)) .andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(MockMvcResultMatchers.jsonPath("$.code").value(400)); } private String toJson(Object object) throws JsonProcessingException { return new ObjectMapper().writeValueAsString(object); } }这类代码 AI 可以生成一个起点,但需要注意几个问题:
- 你们项目的统一返回结构可能不是
code=0; - 参数校验可能由
@Valid实现,也可能在 Service 层处理; - Mock 的 Service 返回值要贴近真实业务;
- 如果是集成测试,不能只 Mock 掉所有依赖;
- 消息发送、日志写入需要单独验证。
第四步:补充状态机校验逻辑
对于状态流转类接口,最好不要把判断散落在多个if else里。可以先定义状态流转表:
public enum AfterSaleStatus { APPLYING, APPROVED, REJECTED, RETURNING, REFUNDED, CLOSED }再用一个简单的 Map 管理合法流转:
public class AfterSaleStatusFlow { private static final Map<AfterSaleStatus, Set<AfterSaleStatus>> ALLOWED_FLOW = Map.of( AfterSaleStatus.APPLYING, Set.of( AfterSaleStatus.APPROVED, AfterSaleStatus.REJECTED, AfterSaleStatus.CLOSED ), AfterSaleStatus.APPROVED, Set.of( AfterSaleStatus.RETURNING, AfterSaleStatus.REFUNDED, AfterSaleStatus.CLOSED ), AfterSaleStatus.RETURNING, Set.of( AfterSaleStatus.REFUNDED, AfterSaleStatus.CLOSED ), AfterSaleStatus.REJECTED, Set.of(), AfterSaleStatus.REFUNDED, Set.of(), AfterSaleStatus.CLOSED, Set.of() ); public static boolean canTransfer(AfterSaleStatus current, AfterSaleStatus target) { if (current == null || target == null) { return false; } return ALLOWED_FLOW.getOrDefault(current, Set.of()).contains(target); } }对应的单元测试可以这样写:
class AfterSaleStatusFlowTest { @Test void applying_can_transfer_to_approved() { assertTrue(AfterSaleStatusFlow.canTransfer( AfterSaleStatus.APPLYING, AfterSaleStatus.APPROVED )); } @Test void applying_cannot_transfer_to_refunded() { assertFalse(AfterSaleStatusFlow.canTransfer( AfterSaleStatus.APPLYING, AfterSaleStatus.REFUNDED )); } @Test void refunded_cannot_transfer_to_any_status() { assertFalse(AfterSaleStatusFlow.canTransfer( AfterSaleStatus.REFUNDED, AfterSaleStatus.CLOSED )); } }这部分很适合让 Gemini 3.5-flash 帮忙列完整组合。人工只需要确认业务规则是否正确。
第五步:让 AI 生成边界条件,但不要直接相信
可以继续给模型一个更聚焦的 Prompt:
请基于以下接口,补充容易遗漏的边界测试场景。 重点关注: 1. 空值; 2. 非法枚举; 3. 终态修改; 4. 重复请求; 5. 售后单和订单不匹配; 6. 操作日志; 7. 消息发送; 8. 并发更新。 请按“风险点 - 测试场景 - 验证方式”输出表格。输出通常会包括:
| 风险点 | 测试场景 | 验证方式 |
|---|---|---|
| 非法枚举 | status=UNKNOWN | 返回参数错误 |
| 重复请求 | 同一状态重复提交 | 根据幂等规则判断成功或失败 |
| 并发更新 | 两个请求同时修改状态 | 版本号或行锁保证状态一致 |
| 消息重复 | REFUNDED 重复提交 | 不应重复发送退款消息 |
| 日志缺失 | 更新成功后未记录日志 | 查询操作日志表 |
其中“重复请求是否成功”不能由 AI 决定,需要看系统幂等设计。如果业务要求重复提交返回成功,那测试预期就不能写失败。
模型能力对比:不同任务可以分工
在接口测试这个场景里,我一般这样使用不同模型:
| 模型 | 更适合的任务 | 注意点 |
|---|---|---|
| Gemini 3.5-flash | 结构化整理、测试用例表格、边界条件补充 | 需要给清楚字段和规则 |
| ChatGPT | 代码草稿、测试代码示例、方案讨论 | 生成代码要按项目规范调整 |
| Claude | 长 PRD、接口文档、需求说明归纳 | 适合处理长上下文,但仍需核对细节 |
| DeepSeek | 中文技术解释、Java 代码理解、异常路径拆解 | 适合中文开发文档和推理型问题 |
多模型对比的意义不是选一个“永远正确”的模型,而是发现遗漏点。比如一个模型会提醒并发更新,另一个模型会提醒消息重复投递,还有一个模型会提醒状态枚举非法值。最终要合并成自己的测试清单。
AI 输出怎么验证
1. 对照真实需求和接口文档
AI 生成的用例一定要和 PRD、接口文档、状态机说明对齐。尤其是:
- 状态能否跳转;
- 重复请求如何处理;
- 终态是否允许备注修改;
- 失败时是否写日志;
- 消息是否同步发送还是异步发送。
2. 对照代码实现
测试用例不能只停留在表格里,要看代码实际路径:
Controller 参数校验 ↓ Service 业务校验 ↓ Repository 更新状态 ↓ OperationLog 写入 ↓ MessageProducer 发送消息如果某个环节没有覆盖,就说明测试还不完整。
3. 对照数据库状态
接口返回成功不代表业务真的正确。建议至少验证:
- 售后单状态是否更新;
- 更新时间是否变化;
- 操作人是否记录;
- 操作日志是否生成;
- 退款消息是否进入消息表或队列。
4. 对照异常和并发场景
对于订单、库存、售后这类状态型业务,并发非常重要。可以补充伪代码:
pseudo
prepare afterSale.status = APPROVED start thread A: update status to RETURNING start thread B: update status to CLOSED wait all threads finished assert only one update success assert final status is valid assert operation log count is correct assert message is not duplicated如果系统使用乐观锁,可以检查 version 字段;如果使用数据库行锁,则要关注事务范围和锁等待时间。
多模型工具的判断标准
如果团队准备把多模型工具放进研发流程,可以从几个实际维度判断:
- 是否方便同一 Prompt 对比输出:便于发现不同模型的关注点;
- 是否支持长文本输入:需求文档、接口说明、日志通常比较长;
- 是否能稳定输出 Markdown 表格:方便复制到测试文档或缺陷单;
- 是否支持历史上下文管理:同一个接口可能要多轮补充;
- 是否便于脱敏使用:不要直接上传内部代码、客户数据和生产日志。
工具只是辅助,真正重要的是团队有没有统一的输入模板、Review 流程和验证标准。
风险边界:哪些内容不要直接交给 AI
在企业项目里,以下内容需要谨慎处理:
- 未脱敏的生产日志;
- 客户手机号、地址、订单号等敏感数据;
- 公司内部完整源码;
- 私有接口签名和鉴权规则;
- 密钥、Token、证书;
- 未公开的业务策略;
- 涉及安全风控的规则细节。
更稳妥的做法是:保留字段结构、状态流转、错误现象和脱敏样例,把真实用户信息、内部路径、密钥和业务敏感字段去掉。
FAQ:常见误区
1. AI 生成的测试用例可以直接用吗?
不建议直接使用。AI 生成的是初稿,需要结合真实需求、代码实现、数据库状态和团队测试规范进行 Review。
2. AI 生成的 Java 测试代码能直接提交吗?
通常需要修改。项目里的返回结构、异常码、Mock 方式、测试基类、数据库初始化方式都可能不同,生成代码只能作为参考。
3. 单一模型够不够?
普通接口的测试用例整理,一个模型通常够用。复杂业务、状态流转、并发更新、消息一致性这类场景,可以用多模型交叉检查遗漏点。
4. 如何避免 AI 编造接口或字段?
Prompt 里要明确“只能基于已提供字段生成,不要新增不存在的字段”。输出后还要对照 Swagger、代码 DTO 和接口文档核验。
5. 测试用例生成后怎么验证质量?
可以看四点:是否覆盖正常流程、是否覆盖异常参数、是否覆盖业务边界、是否能落到自动化测试或手工验证步骤上。
总结
Gemini 3.5-flash 用在 Java 接口测试场景里,比较适合做需求结构化、测试用例初稿、边界条件补充和 Markdown 表格整理。它能减少重复整理工作的时间,但不能替代开发和测试对业务规则的判断。
比较稳妥的使用方式是:先选一个高频接口,把字段、状态、规则和预期写清楚;再用 Prompt 约束输出格式;随后由人工 Review,并通过单元测试、接口测试、数据库校验和并发验证确认结果。重要接口可以用多模型交叉检查,但最终是否可上线,仍然要以代码、测试和业务验收为准。
