- 测试
- 后端
【免费下载链接】wiremock
A tool for mocking HTTP services
本文以仓库根目录下的 AGENTS.md 为骨架,逐条拆解 WireMock 为 AI 编码代理(以及所有贡献者)制定的开发规则:如何用
./gradlew正确驱动构建、如何定位 WebSocket/消息相关代码包,以及消息领域代码必须遵守的 5 条编码铁律。读完本文,你将能像 WireMock 核心维护者一样,在wiremock-core中正确地修改、测试与提交消息/WebSocket 相关代码,并理解这些规则背后的源码与工程化依据。
一、规则文档定位:AGENTS.md 是给"代理与协作者"的入场须知
WireMock(README.md 中自我定位为 "A tool for mocking HTTP services")是一个多模块 Gradle 工程,仓库根目录同时存在 CLAUDE.md、CONTRIBUTING.md 与 AGENTS.md 三份面向不同读者的文档。其中AGENTS.md 篇幅最短(全文仅 20 行),但信息密度极高:它不是介绍 WireMock 的 API 用法,而是聚焦"开发这台机器该怎么正确运转"——即构建命令的强制形态、消息/WebSocket 代码的物理位置,以及这些代码的编码红线。
从 settings.gradle.kts 可以看到,仓库由wiremock-core、wiremock-standalone、wiremock-junit4、wiremock-junit5、wiremock-jetty、wiremock-httpclient-apache5、wiremock-url系列等多个子项目构成。AGENTS.md 关心的正是其中承载 WebSocket 与消息(Messaging)能力的核心模块,因此它更像一份针对特定子系统(消息/WebSocket)的"围栏式"开发规范:明确告诉代理哪些包可以动、动的时候要遵守什么。
二、构建规则:永远通过./gradlew启动构建
AGENTS.md 第一条规则只有一句话:
Always use
./gradlewto run the build. This ensures that the correct version of Gradle is used and all plugins are applied.
(始终使用./gradlew运行构建,以确保使用正确的 Gradle 版本并应用所有插件。)
2.1 为什么必须用 wrapper?
仓库根目录与 buildSrc/gradlew、perf-test/gradlew 都自带 Gradle Wrapper。通过 gradle/wrapper/gradle-wrapper.properties 可以看到当前锁定的发行版为Gradle 9.8.0。如果代理直接调用本机安装的gradle,版本一旦不一致,就会导致构建脚本(如 buildSrc/src/main/kotlin/wiremock.common-conventions.gradle.kts 中应用的 Spotless、Error Prone、Shadow、SonarQube、Maven Publish 等插件)解析失败或行为漂移。
2.2 wrapper 背后实际触发的构建管线
./gradlew会依次加载 settings.gradle.kts 与各子项目的build.gradle.kts。以核心模块 wiremock-core/build.gradle.kts 为例,其依赖体系(api(libs.json.schema.validator)、api(libs.json.unit.core)、implementation(libs.apache.http5.client)等)全部由统一版本目录 gradle/libs.versions.toml 管理。常见的开发循环命令包括:
# 编译并运行所有单元/集成测试(含 testFixtures) ./gradlew build # 仅编译核心模块 ./gradlew :wiremock-core:compileJava # 运行指定测试类 ./gradlew :wiremock-core:test --tests "com.github.tomakehurst.wiremock.*" # 代码格式化检查(Spotless) ./gradlew spotlessCheck从 wiremock.common-conventions.gradle.kts 可以看到测试任务的额外约定:JVM 时区被强制设为Australia/Sydney(便于暴露时区相关 bug)、使用 JUnit Platform、ignored/**目录下的测试会被排除、CI 上串行执行(maxParallelForks = 1)而非 CI 环境并行 3 个 fork;Spotless 还负责 Java(Google Java Format 1.17.0)、Gradle Kotlin/Groovy 脚本与 JSON 的统一格式化。
从源码结构看,这份构建规则的本质是:把"正确环境"的决策权完全交给 wrapper,代理无需(也不应)自行判断本机 Gradle 版本。
三、代码定位:消息/WebSocket 功能的三个落点
AGENTS.md 明确列出了三类包含消息/WebSocket 功能的包。需要特别说明的是:经核对仓库实际结构,文档中描述的websocket/包在最新代码中位于message/命名空间之下,本文以实际路径为准:
wiremock-core/src/main/java/com/github/tomakehurst/wiremock/message/websocket/—— WebSocket 会话与消息通道的接口与实现;wiremock-core/src/main/java/com/github/tomakehurst/wiremock/message/—— 整个消息(Messaging)子系统根包(含 channel、sse、stub 映射、触发器、动作等);wiremock-core/src/main/java/com/github/tomakehurst/wiremock/verification/—— 类名含 "Message" 的消息校验/日志相关类。
3.1message/websocket/:通道与会话的骨架
该包目前只有两个类:
- WebSocketMessageChannel.java:实现
RequestInitiatedMessageChannel接口,承载一次 WebSocket 消息通道的id、发起请求(Request)与会话引用。关键行为(见 L63-L80):isOpen():仅当session != null && session.isOpen()时才认为通道开启;sendMessage(Message):会话存在且开启时转发消息,否则静默丢弃(空安全);close():安全关闭会话。
- WebSocketSession.java:仅含
isOpen()、sendMessage(Message)、close()三个方法的接口——这正是 AGENTS.md 要求"代码简洁到无需注释"的典型样本。
3.2message/根包:消息子系统的全貌
通过list_files可以确认该根包下还有channel/(ChannelProvider、InMemoryChannelProviderDriver 等)、sse/(SseMessageChannel、SseSession 等)以及大量顶层类:Message、MessageAction、MessageChannel、MessageStubMapping、SendMessageAction、HttpRequestTrigger、IncomingMessageTrigger、ChannelType等。也就是说,WebSocket 只是消息通道的一种实现(ChannelType.WEBSOCKET),旁边还并列着 SSE 等通道,这与 WebSocketMessageChannel.getType() 返回ChannelType.WEBSOCKET的实现相互印证。
3.3verification/中的 Message 类:消息日志与校验
该包内与消息相关的类包括 InMemoryMessageJournal.java、StoreBackedMessageJournal.java、MessageServeEvent.java、MessageVerificationResult.java 等,承担消息的"发出记录—校验比对"链路。对应地,测试端有 InMemoryMessageJournalTest.java、MessageSerializationTest.java 等验证这一链路。
四、五条编码铁律:消息/WebSocket 代码的改动红线
AGENTS.md 用 5 条规则约束上述包内的一切改动,下面逐条展开并结合源码佐证。
4.1 规则一:不加 Javadoc(No Javadoc)
禁止对类、接口、方法、字段添加 Javadoc 注释。这条规则与 WireMock 全局的 Javadoc 策略并不冲突——wiremock.common-conventions.gradle.kts 中tasks.javadoc仅是对发布产物做 doclint 处理,与源码注释无关。消息包内的代码(如WebSocketSession接口)刻意保持"零注释、自解释命名"的极简风格,正是这条规则的执行结果。
提示:AGENTS.md 只约束消息/WebSocket 包;仓库其他模块仍正常使用 Javadoc(例如
org.wiremock.annotations.PublishedAPI等注解体系),改动前先确认自己所在包是否在规则覆盖范围内。
4.2 规则二:不加注释(No Comments)
除极少数"无法通过命名与代码结构表达清楚的非显然决策"外,禁止行内注释。其意图是迫使开发者把意图写进命名与结构里:比如WebSocketMessageChannel.sendMessage()中对session为空的静默处理,靠isOpen()的空安全判断(L69-L73)本身就说明了语义,无需注释解释。
4.3 规则三:必须使用 import(Always import)
禁止使用全限定类名,一律通过import语句引用。这一点在测试代码中贯彻得很彻底——例如 EntityDefinitionTest.java 密集使用静态导入(import static net.javacrumbs.jsonunit.JsonMatchers.jsonEquals等),全部通过 import 引入;生产代码同样如此,WebSocketMessageChannel.java 的导入区就是标准范例。这与 wiremock.common-conventions.gradle.kts 中强制开启的NullAway(ERROR 级别)配合,保证类型可空性注解在 import 体系下可被静态分析正确追踪。
4.4 规则四:JSON 断言统一使用 JsonUnit(而非 JSONAssert)
测试中的 JSON 断言必须使用net.javacrumbs.jsonunit.JsonMatchers提供的jsonEquals、jsonPartEquals等匹配器,而不是JSONAssert。
- 依赖来源:JsonUnit 通过 wiremock-core/build.gradle.kts 的
api(libs.json.unit.core)声明(对应 gradle/libs.versions.toml 中的json.unit.core版本目录项),因此在核心模块的测试中可直接静态导入使用。 - 实战范例:ChannelProviderApiAcceptanceTest.java 顶部导入
jsonEquals、jsonPartEquals;L64-L73 用jsonEquals整段比对响应 JSON;L109-L113 用jsonPartEquals精确断言meta.total与channelProviders[0].name等局部字段。 - 为何如此:JsonUnit 的匹配器天然容忍 JSON 的键序差异、数字表示差异与空白差异,专为"语义相等"设计,而 JSONAssert 的严格模式对键序等表面差异更敏感。消息/WebSocket 测试经常要断言服务端生成的 JSON 载荷,JsonUnit 能显著降低脆弱断言。
4.5 规则五:JSON 期望值一律使用多行文本块
测试中出现的期望 JSON 值,必须写成美观打印(pretty-printed)的多行字符串,并前置// language=json语言提示注释(供 IDE 提供 JSON 语法高亮与校验)。
- 典型写法(来自 EntityDefinitionTest.java):
String expectedJson = // language=json """ { "compression": "gzip", "base64Data": "%s" } """ .formatted(base64); assertThat(Json.write(entity), jsonEquals(expectedJson));- 两个注意点:
// language=json必须紧贴文本块首行(如 ChannelProviderApiAcceptanceTest.java 所示),否则 IDE 无法识别语言;- Java 文本块(Java 15+ 正式特性)是本仓库测试的主流写法,配合
.formatted(...)做占位符注入(如上面的%s);项目 Java 版本基线为 17(见 wiremock.common-conventions.gradle.kts),完全支持这一语法。
该写法在仓库中应用广泛:src/test/java下至少有ChannelProviderApiAcceptanceTest、CustomChannelDriverAcceptanceTest、SseAcceptanceTest、WebsocketEntityDefinitionAcceptanceTest、MessageChannelApiValidationAcceptanceTest等十余个测试类使用// language=json。
五、这些规则在 CI 与质量门禁中的落地
AGENTS.md 的五条规则并非孤立的文档约束,而是与仓库的自动化质量体系互为表里:
- 格式统一:Spotless(Google Java Format)在
spotlessCheck中强制 Java 文件格式,五条规则中的"无注释/简洁代码"与格式化共同保证消息包代码的视觉一致性; - 空安全静态分析:Error Prone + NullAway 以 ERROR 级别运行(wiremock.common-conventions.gradle.kts),
WebSocketMessageChannel中@Nullable session的写法正是 NullAway 约束下的产物,且此类代码必须配好 import 才能通过编译检查; - 测试卫生:测试任务固定时区、排除
ignored/**、输出失败详情(TestExceptionFormat.FULL),让 JsonUnit 断言的失败信息在 CI 上可读可查。
六、给 AI 代理的实操清单
综合全文,代理在改动消息/WebSocket 相关代码前,应按以下清单自查:
- 构建:任何编译、测试、打包一律
./gradlew ...,不调用系统 Gradle; - 定位:确认改动是否落入
wiremock-core/src/main/java/com/github/tomakehurst/wiremock/message/(含websocket/、channel/、sse/子包)或verification/中名称含 "Message" 的类; - 代码风格:不加 Javadoc、不加行内注释(除非决策非显然且无法用命名表达)、只用 import;
- 测试:JSON 断言只用
net.javacrumbs.jsonunit.JsonMatchers;期望 JSON 一律// language=json+ 多行文本块 +.formatted()注入动态值; - 验证:提交前跑
./gradlew spotlessCheck与相关模块测试,确认没有破坏时区固定、测试排除等既有约定。
七、小结
AGENTS.md 是 WireMock 仓库中一份"小而硬"的代理开发规范:一条构建命令红线 + 三个代码落点 + 五条编码铁律。它的价值不在于篇幅,而在于把消息/WebSocket 这个高风险子系统(涉及长连接会话、异步消息、JSON 序列化与校验)的工程纪律固化成了可执行的规则。对于阅读本文的开发者与 AI 代理而言,遵循这份规范,意味着你的改动将无缝融入 WireMock 的构建、质量与测试体系——这既是贡献者契约,也是高效协作的起点。
- 测试
- 后端
【免费下载链接】wiremock
A tool for mocking HTTP services
相关推荐
RabbitMQ rabbitmq-server 仓库开发导航:从 AGENTS.md 读懂多协议消息代理的架构、构建与协作规范
RabbitMQ rabbitmq server 仓库开发导航:从 AGENTS.md 读懂多协议消息代理的架构、构建与协作规范 RabbitMQ 是一个开源的
后端消息队列消息路由rclone AGENTS.md 精读:AI 编码代理协作开发规范、构建测试流程与核心架构约定
rclone AGENTS.md 精读:AI 编码代理协作开发规范、构建测试流程与核心架构约定 rclone 仓库根目录下的 AGENTS.md 是专门写给 A
CLI数据同步对象存储Hugo AGENTS.md 深度解读:写给人类与 AI 智能体的开发规范
Hugo AGENTS.md 深度解读:写给人类与 AI 智能体的开发规范 AGENTS.md 是 Hugo 仓库为代码贡献者(包括 AI 编码智能体)制定的行
开发工具前端CLI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考