当前位置: 首页 > article >正文

自动化测试系列(五) 微服务接口测试-WireMock与契约测试-CDC

微服务接口测试WireMock与契约测试CDC上篇咱们用RestAssured搞定了单体应用的接口测试。但微服务架构下你的服务依赖一堆下游服务怎么测今天聊WireMock模拟和契约测试这是微服务测试的两大杀器。一、微服务测试的困境假设你在开发订单服务它依赖这些下游┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ 用户服务 │ │ 库存服务 │ │ 支付服务 │ │ user-svc │ │ stock-svc │ │ pay-svc │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │ │ └───────────────────┼───────────────────┘ │ ┌──────┴──────┐ │ 订单服务 │ │ order-svc │ └─────────────┘问题1下游服务没就绪你在开发订单服务的创建订单功能但库存服务还在开发中。怎么办等他们做完问题2下游服务不稳定测试环境库存服务经常挂你的测试也跟着红一片。到底是你的bug还是他们的锅问题3接口变更没人通知支付服务改了响应字段从paymentStatus变成了status。你的代码没改线上直接炸。这三个问题WireMock 契约测试能一起解决。二、WireMock你的替身演员WireMock的核心思想在测试时启动一个假的HTTP服务模拟下游的响应。快速上手dependencygroupIdcom.github.tomakehurst/groupIdartifactIdwiremock-jre8/artifactIdversion2.35.0/versionscopetest/scope/dependency场景订单服务依赖库存服务ExtendWith(SpringExtension.class)SpringBootTest(webEnvironmentSpringBootTest.WebEnvironment.RANDOM_PORT)classOrderServiceIntegrationTest{LocalServerPortprivateintorderServicePort;// 启动WireMock服务器模拟库存服务RegisterExtensionstaticWireMockExtensionstockServiceMockWireMockExtension.newInstance().options(wireMockConfig().dynamicPort()).build();AutowiredprivateOrderServiceorderService;DynamicPropertySourcestaticvoidconfigureProperties(DynamicPropertyRegistryregistry){// 让订单服务连上WireMock而不是真实的库存服务registry.add(inventory.service.url,stockServiceMock::baseUrl);}TestDisplayName(库存充足时订单创建成功)voidshouldCreateOrderWhenStockSufficient(){// 配置WireMock当收到库存查询请求时返回有库存stockServiceMock.stubFor(get(urlPathEqualTo(/api/stock/ITEM-001)).willReturn(aResponse().withStatus(200).withHeader(Content-Type,application/json).withBody( { sku: ITEM-001, availableQuantity: 100, reservedQuantity: 5 } )));// 配置扣减库存的mock响应stockServiceMock.stubFor(post(urlPathEqualTo(/api/stock/deduct)).withRequestBody(containing(ITEM-001)).willReturn(aResponse().withStatus(200).withBody( { success: true, deductedQuantity: 2, remainingQuantity: 98 } )));// 执行测试OrderRequestrequestnewOrderRequest(ITEM-001,2);OrderResultresultorderService.createOrder(request);// 验证assertThat(result.isSuccess()).isTrue();assertThat(result.getOrderId()).isNotNull();// 验证订单服务确实调了库存服务的扣减接口stockServiceMock.verify(postRequestedFor(urlPathEqualTo(/api/stock/deduct)).withRequestBody(containing(ITEM-001)));}TestDisplayName(库存不足时订单创建失败)voidshouldFailWhenStockInsufficient(){// 模拟库存不足stockServiceMock.stubFor(get(urlPathEqualTo(/api/stock/ITEM-001)).willReturn(aResponse().withStatus(200).withBody( { sku: ITEM-001, availableQuantity: 1, reservedQuantity: 0 } )));OrderRequestrequestnewOrderRequest(ITEM-001,5);// 要买5个但只有1个// 期望抛出库存不足异常assertThatThrownBy(()-orderService.createOrder(request)).isInstanceOf(InsufficientStockException.class).hasMessageContaining(库存不足);}}WireMock 的核心API速查// 匹配请求get(urlEqualTo(/api/users/1))get(urlPathEqualTo(/api/users/1))get(urlPathMatching(/api/users/\\d))post(urlEqualTo(/api/orders)).withHeader(Content-Type,containing(json)).withRequestBody(equalToJson({\name\:\Alice\}))// 构造响应willReturn(aResponse().withStatus(200).withHeader(Content-Type,application/json).withBody({\id\:1}).withFixedDelay(500))// 模拟延迟500ms// 验证请求是否被调用verify(getRequestedFor(urlEqualTo(/api/users/1)))verify(2,postRequestedFor(urlEqualTo(/api/orders)))// 验证调了2次verify(0,deleteRequestedFor(anyUrl()))// 验证没调过delete三、WireMock 的进阶玩法1. 从文件加载响应适合大JSON// 把响应体放在 src/test/resources/wiremock/stock-available.jsonstockServiceMock.stubFor(get(urlPathEqualTo(/api/stock/ITEM-001)).willReturn(aResponse().withStatus(200).withBodyFile(wiremock/stock-available.json))// 从文件加载);2. 模拟故障场景TestDisplayName(库存服务超时订单服务应该降级)voidshouldFallbackWhenStockServiceTimeout(){// 模拟超时stockServiceMock.stubFor(get(urlPathEqualTo(/api/stock/ITEM-001)).willReturn(aResponse().withStatus(200).withFixedDelay(10000))// 10秒延迟触发超时);// 验证降级逻辑OrderResultresultorderService.createOrder(request);assertThat(result.isSuccess()).isTrue();// 降级后仍然成功assertThat(result.isStockChecked()).isFalse();// 但跳过了库存检查}TestDisplayName(库存服务返回500订单服务应该重试)voidshouldRetryWhenStockServiceError(){// 模拟500错误stockServiceMock.stubFor(get(urlPathEqualTo(/api/stock/ITEM-001)).willReturn(aResponse().withStatus(500)));// 验证重试次数assertThatThrownBy(()-orderService.createOrder(request)).isInstanceOf(StockServiceException.class);// 验证调了3次重试2次 原始1次stockServiceMock.verify(3,getRequestedFor(urlPathEqualTo(/api/stock/ITEM-001)));}3. 有状态的Mock模拟流程TestDisplayName(库存扣减后查询数量应该减少)voidshouldReflectDeductedStock(){// 初始状态有100个stockServiceMock.stubFor(get(urlPathEqualTo(/api/stock/ITEM-001)).inScenario(Stock Deduction).whenScenarioStateIs(Scenario.STARTED).willReturn(aResponse().withBody({\availableQuantity\: 100})).willSetStateTo(Deducted));// 扣减后状态剩98个stockServiceMock.stubFor(get(urlPathEqualTo(/api/stock/ITEM-001)).inScenario(Stock Deduction).whenScenarioStateIs(Deducted).willReturn(aResponse().withBody({\availableQuantity\: 98})));// 先查询StockInfobeforestockClient.queryStock(ITEM-001);assertThat(before.getAvailableQuantity()).isEqualTo(100);// 扣减stockClient.deduct(ITEM-001,2);// 再查询StockInfoafterstockClient.queryStock(ITEM-001);assertThat(after.getAvailableQuantity()).isEqualTo(98);}四、契约测试接口变更的防火墙WireMock解决了测试时模拟下游的问题但还有一个问题下游服务改了接口怎么及时发现这就是**消费者驱动契约测试Consumer-Driven Contract, CDC**要解决的。核心思想消费者订单服务 契约文件 提供者库存服务 │ │ │ │ 我期望接口长这样 │ │ │ ───────────────────── │ │ │ │ │ │ │ 验证你的实现是否符合契约 │ │ │ ─────────────────────│ │ │ │ │ │ 符合 / 不符合 │ │ │ ─────────────────────│简单说消费者定义我期望你怎么响应提供者验证我是不是按这个实现的。Spring Cloud Contract 实战1. 消费者端订单服务定义契约在订单服务的src/test/resources/contracts/下创建契约文件// shouldReturnStockInfo.groovypackagecontracts.stock org.springframework.cloud.contract.spec.Contract.make{request{methodGETurl/api/stock/ITEM-001headers{contentType(applicationJson())}}response{status200headers{contentType(applicationJson())}body([sku:ITEM-001,availableQuantity:100,reservedQuantity:5])}}2. 订单服务生成Stub并发布!-- pom.xml --plugingroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-contract-maven-plugin/artifactIdversion4.0.0/versionextensionstrue/extensionsconfigurationbaseClassForTestscom.example.BaseContractTest/baseClassForTests/configuration/plugin运行mvn clean install会自动根据契约文件生成测试代码生成Stub JAR包含WireMock映射发布到Maven仓库3. 库存服务提供者验证契约!-- 库存服务引入契约依赖 --dependencygroupIdcom.example/groupIdartifactIdorder-service/artifactIdversion1.0.0/versionclassifierstubs/classifierscopetest/scope/dependencySpringBootTest(webEnvironmentSpringBootTest.WebEnvironment.MOCK)AutoConfigureMessageVerifierclassStockServiceContractTest{AutowiredprivateStockControllerstockController;// 自动加载订单服务定义的契约验证库存服务的实现TestvoidvalidateStockContract(){// 契约测试框架自动执行}}Pact更轻量的契约测试方案如果不用Spring生态Pact是个更通用的选择。消费者端生成契约dependencygroupIdau.com.dius.pact.consumer/groupIdartifactIdjunit5/artifactIdversion4.6.0/versionscopetest/scope/dependencyExtendWith(PactConsumerTestExt.class)PactTestFor(providerNamestock-service)classStockServicePactTest{Pact(consumerorder-service)publicRequestResponsePactstockQueryPact(PactDslWithProviderbuilder){returnbuilder.given(stock exists for ITEM-001).uponReceiving(query stock for ITEM-001).path(/api/stock/ITEM-001).method(GET).willRespondWith().status(200).body(newPactDslJsonBody().stringType(sku,ITEM-001).integerType(availableQuantity,100).integerType(reservedQuantity,5)).toPact();}PactTestFor(pactMethodstockQueryPact)TestvoidshouldQueryStock(MockServermockServer){// 使用Pact生成的Mock服务测试消费者代码StockClientclientnewStockClient(mockServer.getUrl());StockInfoinfoclient.queryStock(ITEM-001);assertThat(info.getSku()).isEqualTo(ITEM-001);assertThat(info.getAvailableQuantity()).isPositive();}}运行后会在target/pacts/生成契约JSON文件{consumer:{name:order-service},provider:{name:stock-service},interactions:[{description:query stock for ITEM-001,providerState:stock exists for ITEM-001,request:{method:GET,path:/api/stock/ITEM-001},response:{status:200,body:{sku:ITEM-001,availableQuantity:100,reservedQuantity:5}}}]}提供者端验证契约Provider(stock-service)PactFolder(pacts)classStockServiceProviderVerificationTest{TestTemplateExtendWith(PactVerificationInvocationContextProvider.class)voidpactVerificationTestTemplate(PactVerificationContextcontext){context.verifyInteraction();}BeforeEachvoidbefore(PactVerificationContextcontext){context.setTarget(newHttpTestTarget(localhost,8080));}State(stock exists for ITEM-001)voidstockExistsState(){// 准备测试数据确保ITEM-001有库存stockRepository.save(newStock(ITEM-001,100,5));}}五、WireMock vs 契约测试怎么选场景用WireMock用契约测试单元/集成测试时模拟下游✅❌验证下游接口是否符合预期❌✅接口变更时自动发现不兼容❌✅跨团队协作定义接口规范辅助✅CI流水线中验证契约不行✅最佳实践两者结合开发时用WireMock快速验证提测前用契约测试确保兼容性CI流水线跑契约验证不通过不让合并六、小结今天咱们聊了微服务测试的两大武器工具解决什么问题核心能力WireMock下游服务没就绪/不稳定模拟HTTP服务控制响应内容、延迟、故障Spring Cloud ContractSpring生态的契约测试消费者定义契约提供者自动验证Pact跨语言的契约测试生成契约文件独立验证消费者和提供者一句话总结WireMock让你在孤岛上也能开发测试契约测试让团队之间的接口约定变成可执行的代码。两者结合微服务测试就稳了。你们微服务测试怎么做的用WireMock还是直接连测试环境欢迎聊聊。

相关文章:

自动化测试系列(五) 微服务接口测试-WireMock与契约测试-CDC

微服务接口测试:WireMock与契约测试(CDC)上篇咱们用RestAssured搞定了单体应用的接口测试。但微服务架构下,你的服务依赖一堆下游服务,怎么测?今天聊WireMock模拟和契约测试,这是微服务测试的两…...

Handlebars.js扩展开发终极指南:自定义Helper与Decorator创建技巧

Handlebars.js扩展开发终极指南:自定义Helper与Decorator创建技巧 【免费下载链接】handlebars.js Minimal templating on steroids. 项目地址: https://gitcode.com/gh_mirrors/ha/handlebars.js Handlebars.js作为一款功能强大的模板引擎,为开发…...

AI编程助手全景图:从GitHub Copilot到本地部署,开发者如何高效选型

1. 项目概述:一份AI编程助手全景图如果你是一名开发者,最近两年一定被各种AI编程工具轮番轰炸过。从GitHub Copilot横空出世,到ChatGPT写代码,再到各种本地化部署的代码模型,感觉不跟上这波潮流,写代码的效…...

别只刷题了!用PTA L2真题拆解C++ STL:set、map、vector在算法竞赛中的实战技巧

用PTA L2真题拆解C STL:set、map、vector在算法竞赛中的实战技巧 在算法竞赛中,熟练掌握C标准模板库(STL)是提升编码效率的关键。本文将通过PTA团体程序设计天梯赛L2真题,深入剖析set、map和vector三大核心容器的实战应…...

React Google Maps API搜索与自动完成:Autocomplete和StandaloneSearchBox使用详解

React Google Maps API搜索与自动完成:Autocomplete和StandaloneSearchBox使用详解 【免费下载链接】react-google-maps-api React Google Maps API 项目地址: https://gitcode.com/gh_mirrors/re/react-google-maps-api React Google Maps API是一个强大的库…...

第36篇:Vibe Coding时代:LangGraph 自动生成 README 实战,解决 AI 项目交付后没人知道怎么运行的问题

第36篇:Vibe Coding时代:LangGraph 自动生成 README 实战,解决 AI 项目交付后没人知道怎么运行的问题 一、问题场景:Agent 生成了项目,但没有运行说明 AI Coding Agent 很擅长生成代码文件。 比如: main.py requirements.txt test_main.py config.py但是很多时候它没有…...

VOIPAC iMX8M工业级开发套件评测与应用指南

1. VOIPAC iMX8M工业级开发套件概览VOIPAC iMX8M工业级开发套件是一款基于NXP i.MX 8M四核Cortex-A53处理器的嵌入式开发平台,专为工业应用场景设计。这套开发板的核心是"iMX8M Industrial Pro"系统模块(SOM),标配2GB RAM和16GB eMMC闪存&…...

Python配置管理新范式:基于装饰器的Tanuki库实践指南

1. 项目概述:一个轻量级、高可配的Python配置管理库 在Python项目开发中,配置管理是个看似简单、实则暗藏玄机的环节。从最简单的 config.py 里写几个变量,到使用环境变量、YAML/JSON文件,再到引入复杂的配置中心,每…...

Lichess Mobile多语言支持:如何实现147种语言的国际化

Lichess Mobile多语言支持:如何实现147种语言的国际化 【免费下载链接】mobile Lichess mobile app 项目地址: https://gitcode.com/gh_mirrors/mobile6/mobile Lichess Mobile作为一款全球流行的开源国际象棋应用,通过强大的国际化架构支持147种…...

TermuxBlack故障排除:常见安装问题和解决方案完整清单

TermuxBlack故障排除:常见安装问题和解决方案完整清单 【免费下载链接】TermuxBlack Termux repository for hacking tools and packages 项目地址: https://gitcode.com/gh_mirrors/te/TermuxBlack TermuxBlack是一个专注于提供黑客工具和软件包的Termux仓库…...

ARM Trace Analyzer:指令追踪与调试核心技术解析

1. ARM Trace Analyzer技术概览指令追踪技术是现代处理器调试与性能分析的基石,而ARM Trace Analyzer作为CoreSight调试架构的核心组件,其设计哲学体现了硬件级调试的前沿思想。想象一下,当处理器以GHz频率运行时,工程师需要在不影…...

any-listen安全与隐私保护:构建可信赖的私有音乐服务

any-listen安全与隐私保护:构建可信赖的私有音乐服务 【免费下载链接】any-listen A cross-platform private music playback service 项目地址: https://gitcode.com/gh_mirrors/an/any-listen 在数字音乐时代,隐私保护已成为用户最关心的核心需…...

Bottleneck性能优化:7个最佳实践让你的应用速度提升300%

Bottleneck性能优化:7个最佳实践让你的应用速度提升300% 【免费下载链接】bottleneck Job scheduler and rate limiter, supports Clustering 项目地址: https://gitcode.com/gh_mirrors/bo/bottleneck Bottleneck是一款轻量级且零依赖的任务调度器和速率限制…...

PaperForge:模块化AI提示词框架,赋能学术写作与专利转化

1. 项目概述与核心价值如果你是一名研究生、科研人员,或者像我一样,经常需要和学术论文、技术专利打交道,那你一定体会过那种“词穷”和“逻辑混乱”的痛苦。初稿写出来像流水账,翻译出来的英文读着别扭,好不容易写完又…...

wait-on 终极指南:如何轻松等待文件和网络资源就绪

wait-on 终极指南:如何轻松等待文件和网络资源就绪 【免费下载链接】wait-on wait-on is a cross-platform command line utility and Node.js API which will wait for files, ports, sockets, and http(s) resources to become available 项目地址: https://git…...

团队管理工具现代化重构:从可定制数据模型到实时协同的架构实践

1. 项目概述:一个团队管理工具的“刷新”意味着什么?最近在GitHub上看到一个挺有意思的项目,叫loLollipop/team-manage-refresh。光看这个标题,可能很多人会想,这不就是一个团队管理工具吗?市面上这类工具多…...

Keyboard Cowboy代码架构解析:Swift开发的优秀实践

Keyboard Cowboy代码架构解析:Swift开发的优秀实践 【免费下载链接】KeyboardCowboy :keyboard: The missing keyboard shortcut utility for macOS 项目地址: https://gitcode.com/gh_mirrors/ke/KeyboardCowboy Keyboard Cowboy是一款专为macOS设计的键盘快…...

油猴脚本工具箱:AI搜索增强、双语阅读与网页优化实战

1. 项目概述:一个油猴脚本的实用工具箱如果你和我一样,是个重度浏览器用户,每天要在各种网页间来回切换,处理信息、查找资料,那你肯定也遇到过不少“网页体验不够好”的瞬间。比如,想在搜索引擎结果页快速调…...

基于向量数据库与语义检索的AI记忆增强工具Memok-AI深度解析

1. 项目概述:一个面向记忆增强的AI工具最近在GitHub上闲逛,发现了一个挺有意思的项目,叫galaxy8691/memok-ai。乍一看这个名字,memok很容易让人联想到 “Memory” 和 “OK” 的组合,直译过来就是“记忆没问题”。点进去…...

LLMs-from-scratch-CN性能优化技巧:从FLOPS分析到高效注意力实现

LLMs-from-scratch-CN性能优化技巧:从FLOPS分析到高效注意力实现 【免费下载链接】LLMs-from-scratch-CN LLMs-from-scratch项目中文翻译 项目地址: https://gitcode.com/gh_mirrors/llm/LLMs-from-scratch-CN LLMs-from-scratch-CN是一个专注于LLM模型构建与…...

Devon:AI驱动的研发智能体实战,重塑软件开发工作流

1. 项目概述:Devon,一个重新定义AI驱动的研发工作流如果你和我一样,长期在软件研发一线摸爬滚打,那你肯定对“上下文切换”这个词深恶痛绝。从写代码到查文档,从跑测试到部署上线,再到和同事沟通需求&#…...

gh_mirrors/in/invoice部署实战:从开发到生产环境的完整迁移指南

gh_mirrors/in/invoice部署实战:从开发到生产环境的完整迁移指南 【免费下载链接】invoice Collaboration with wangxupeng(https://github.com/wangxupeng) 项目地址: https://gitcode.com/gh_mirrors/in/invoice gh_mirrors/in/invoice是一个基于YOLOv3CRN…...

10个 wait-on 实用技巧:从基础到高级的完整教程

10个 wait-on 实用技巧:从基础到高级的完整教程 【免费下载链接】wait-on wait-on is a cross-platform command line utility and Node.js API which will wait for files, ports, sockets, and http(s) resources to become available 项目地址: https://gitcod…...

DAC与数字电位器的核心差异与工程选型指南

1. DAC与数字电位器的本质差异在电子系统设计中,数字模拟转换器(DAC)和数字电位器都是实现数字信号控制模拟输出的关键器件,但两者的工作原理和适用场景存在本质区别。我从业十余年,见过太多工程师因为选型不当导致项目返工的情况&#xff0c…...

终极指南:Ralph for Claude Code开发循环异常检测与告警阈值设置全攻略

终极指南:Ralph for Claude Code开发循环异常检测与告警阈值设置全攻略 【免费下载链接】ralph-claude-code Autonomous AI development loop for Claude Code with intelligent exit detection 项目地址: https://gitcode.com/GitHub_Trending/ra/ralph-claude-c…...

Snap.Hutao终极使用指南:专业开源原神工具箱完全解析

Snap.Hutao终极使用指南:专业开源原神工具箱完全解析 【免费下载链接】Snap.Hutao 实用的开源多功能原神工具箱 🧰 / Multifunctional Open-Source Genshin Impact Toolkit 🧰 项目地址: https://gitcode.com/GitHub_Trending/sn/Snap.Huta…...

基于skill-mcp-builder快速构建生产级MCP服务器:从协议到实践

1. 项目概述与核心价值如果你正在为AI助手(比如Claude Code、Cursor、或是Gemini CLI)开发工具,并且厌倦了为每个平台重复编写适配代码,那么你很可能已经听说过Model Context Protocol。MCP,你可以把它理解为AI工具领域…...

纯CSS动画状态检测终极指南:10个实用技巧让你告别JavaScript依赖

纯CSS动画状态检测终极指南:10个实用技巧让你告别JavaScript依赖 【免费下载链接】You-Dont-Need-JavaScript CSS is powerful, you can do a lot of things without JS. 项目地址: https://gitcode.com/gh_mirrors/yo/You-Dont-Need-JavaScript 在现代网页开…...

智能守护系统:LLM驱动的自动化工作流安全架构与实践

1. 项目概述:从“OpenClaw”到“Guardian”的智能守护最近在GitHub上看到一个挺有意思的项目,叫“openclaw-guardian”。光看名字,你可能会有点摸不着头脑——“OpenClaw”是开源之爪?“Guardian”是守护者?这俩词组合…...

agent-skills中的代码简化技术:提升代码可读性和可维护性的实用方法

agent-skills中的代码简化技术:提升代码可读性和可维护性的实用方法 【免费下载链接】agent-skills Production-grade engineering skills for AI coding agents. 项目地址: https://gitcode.com/GitHub_Trending/agentskill/agent-skills agent-skills是一个…...