用于集成的Mocking和stubs
1)为什么需要清洗和存根
与支付提供商,KYC服务,消息经纪人,CRM等的集成使测试缓慢,不稳定且昂贵。清洗/存根允许:- 将服务逻辑与不稳定的环境隔离开来;
- 确定答桉和错误;
- 重现罕见的边界案例(taymauts,429/5xx,非约束性);
- 在本地和CI中快速、可预测地运行测试。
2)术语和分类法
Stub是一个简单的固定响应存根,没有交互检查。
Mock是等待呼叫并对其进行验证的对象(顺序/数量/参数)。
Fake是具有真实行为的简化实现(例如In-Memory存储库)。
Spy是记录实际呼叫的包装纸。
服务虚拟化是一种具有脚本,状态和网络功能的"虚拟"外部服务。
Record/Replay-记录真实流量并随后播放(带过滤器/编辑)。
3)可测试性的建筑模式
端口和适配器(Hexagonal):提供接口集成-易于替换为fake/mock。
反腐败层(ACL):一个模块将外部模型转换为域-少于mok点。
客户端:通过OpenAPI/Protobuf生成→减少手动不匹配。
功能标志和sandbox模式:提供商稳定性的安全密钥/端点。
4)HTTP: 工具和示例
4.1 WireMock (standalone/Java DSL)
JSON-I:json
{
"request": { "method": "POST", "urlPath": "/v1/payouts", "headers": { "Idempotency-Key": { "matches": ".+" } } },
"response": {
"status": 201,
"headers": { "Content-Type": "application/json" },
"jsonBody": { "id": "po_123", "status": "queued" },
"fixedDelayMilliseconds": 80
}
}
Java DSL(具有身体检查和变化):
java stubFor(post(urlEqualTo("/v1/payouts"))
.withHeader("Idempotency-Key", matching(".+"))
.withRequestBody(matchingJsonPath("$.amount", equalTo("100. 00")))
.willReturn(aResponse(). withStatus(201). withHeader("Content-Type","application/json")
.withBody("{\"id\":\"po_123\",\"status\":\"queued\"}")));
4.2 MockServer(扬声器/验证)
json
{
"httpRequest": { "method": "GET", "path": "/v1/wallets/w123" },
"httpResponse": { "statusCode": 200, "headers":[{"name":"Content-Type","values":["application/json"]}],
"body": { "id":"w123","currency":"EUR","balance":0 } }
}
4.3 Hoverfly (middleware, record/replay)
记录与提供商"沙箱"的流量,清除PII,记录为固定。
在模拟模式下,添加变体:200/4xx/5xx、延迟和"flaky"窗口。
4.4 Node (Nock) / Python (responses) / Go (`httptest`)
Nock:
js nock('https://psp. example. com')
.post('/v1/payouts'). reply(201, { id:'po_123', status:'queued' })
.post('/v1/payouts'). reply (409, {code: 'duplicate'}) ;//second call - conflict
Go:
go srv:= httptest. NewServer(http. HandlerFunc(func(w http. ResponseWriter, r http. Request){
if r. Header. Get("Idempotency-Key") == "" { w. WriteHeader(400); return }
w. Header(). Set("Content-Type","application/json")
w. WriteHeader(201); w. Write([]byte(`{"id":"po_123","status":"queued"}`))
}))
defer srv. Close()
5) gRPC/Protobuf
5.1 Staba生成
通过".proto"生成服务器,实现受控响应的方法。
检查元数据(headers)、状态('codes.InvalidArgument`, `codes.DeadlineExceeded`).
Go gRPC fake(片段):go type FakePayouts struct{ pb. UnimplementedPayoutsServer }
func (f FakePayouts) Create(ctx context. Context, in pb. PayoutReq)(pb. PayoutRes,error){
if in. Amount <= 0 { return nil, status. Error(codes. InvalidArgument,"amount>0") }
return &pb. PayoutRes{Id:"po_123", Status:"QUEUED"}, nil
}
5.2 grpcurl用于底片
grpcurl -plaintext -d '{"amount":0}' localhost:50051 payouts. Payouts/Create
6)消息和流: Kafka/RabbitMQ
6.1 Schema-aware洗衣机
使用Schema Registry并在测试中验证Avro/JSON-Schema/Protobuf。
生产者测试:消息符合模式;消费者测试:接受新旧版本。
6.2 Testcontainers(Kafka+Registry示例)
java
KafkaContainer kafka = new KafkaContainer(DockerImageName. parse("confluentinc/cp-kafka:7. 6. 1"));
kafka. start();
//We publish the event and wait for consumption with deduplication by key
6.3负片
复制,顺序排列,延迟交付,"有毒"消息(死信)。
大消息(近极限),未知版本的电路。
7) Contract-aware存根
7.1 Pact (CDC mocks)
消费者形成期望→ pact文件→提供者在展位上验证。
Pact stub Server播放客户端集成测试的期望值。
7.2 OpenAPI/Protobuf → Stabes生成
使莫克服务器脱离规范的工具(例如:Prism,openapi-mock,grpc-mock)。
在规范中包含负面的示例/代码:这也是合同。
8)网络与混乱: 故障模拟
延迟和挤压:固定/分布;检查截止日期和工作时间。
Taymauts/断裂:半开放连接,RST,H2流重置,503/Retry-After。
批量丢失/重复:适用于gRPC/流。
工具:Toxiproxy, MockServer (fault injection), xk6-disruptor, netem in CI。
toxiproxy-cli toxic add psp --type latency --latency 300 --jitter 100
9)数据、秘密和决定论
Redact和合成剂:虚构中没有PII;金钱-decimal/严格的格式。
时间固定:假时钟;"昨天/今天"-控制。
相同性:相同的"Idempotency-Key" →相同的答案。
生成器: 具有透明值(e.g., `test_user_001`).
转化虚拟文件(标签),不要在没有中介的情况下存储"删除"的答桉。
10)CI/CD和环境
矩阵:单位(流程错误)→组件(本地虚拟化)→整合(最低限度moks,Testcontainers)。
工件:pact文件、OpenAPI snapshots、moc服务器日志、PCAP。
并发:唯一的端口/密钥前缀;容器隔离。
门:合同绿色(CDC验证),规范验证值(lint),底片通过。
11)反模式
Moks"复制"了实际服务的缺陷→虚假的信心。接受合同和定期记录/验证的治疗。
整个世界的"Macromoki"在每次测试中都→脆弱性,昂贵的主流。完成薄端口和ACL。
洗涤到需要真正集成的E2E(尤其是使用HMAC/mTLS 的支付/webhooks)。
由于时间/随机/网络比赛而产生的长笛→使用假时钟,确定性座位。
虚构/存储库中的秘密。秘密-仅通过CI秘密存储。
12) iGaming/财务细节
付款/结论:洗涤应支持"Idempotency-Key","Retry-After",HMAC/mTLS,制裁代码和"长期"响应。
Bonus Logic/antifrod:velocity/429脚本,ATO/挑战,TTL的"allow/deny/challenge"风险解决方案。
KYC/AML:对KYC级别,否定词(mismatch,无效文档),带有反重播的webhooks("X-Timestamp"窗口)的sandbox响应。
司法管辖区/特南特:强制性标题"X-Tenant/X-Region",不同的响应配置文件。
13)迷你食谱(spargalka)
重复付款:WireMock "Scenarios"是第一个"201",第二个是"409 duplicate"。
慢速PSP: MockServer的"响应延迟"+检查客户端中的工作时间。
Webhooks:本地HTTP服务器+HMAC签名验证;5秒后的重播不会产生双打。
Kafka副本:两次发布同一消息;亨德勒必须保持相等。
gRPC状态:通过"代码"进行矩阵测试(InvalidArgument,DeadlineExceeded,ResourceExhausted)。
14)准备就绪支票清单
- 分配端口/适配器;集成隐藏在接口后面。
- 对于HTTP/gRPC-存在带有底片的contract-aware stables (Pact/OpenAPI/Proto)。
- 对于经纪人-Testcontainers+Registry;重复/顺序/大消息测试。
- 溷乱:延误、停顿、重置,429/503 with "Retry-After";网络被模拟(Toxiproxy/netem)。
- 没有PII的Fixtura;fake clock;偶然性。
- CI矩阵:单位→组件→整合;文物/合同保存。
- 提供商的沙箱:密钥是分离的,后端是配置的,有运行簿。
- Record/Replay会按计划更新,信托会编辑。
- 控制下测试的flaky和持续时间的度量;生长时的异常。
15) TL;DR
通过薄端口隔离集成,并使用正确的工具执行任务:用于简单桉例的堆栈、用于验证交互的堆栈、用于现实行为的堆栈、用于网络和罕见错误的服务虚拟化和chaos。使洗涤合约(Pact/OpenAPI/Proto),保持假象确定性和没有PII, 模拟延迟/taymauts/429/5xx。在CI中构建金字塔:单位→组件→集成;以红色合同阻止发布。对于支付/KUS路径,请考虑HMAC/mTLS,等效性和负面情况。