name: api-contract-testing description: 使用消费者驱动的契约、模式验证和如Pact等工具验证服务间的API契约。在测试微服务通信、防止破坏性变更或验证OpenAPI规范时使用。
API契约测试
验证API在消费者和提供者之间遵守其契约,无需完整的集成测试。
关键概念
| 术语 | 定义 |
|---|---|
| 消费者 | 调用API的服务 |
| 提供者 | 暴露API的服务 |
| 契约 | 约定的请求/响应格式 |
| Pact | 消费者驱动的契约测试工具 |
| 模式 | 结构定义(OpenAPI, JSON Schema) |
| 代理 | 契约的中央存储库 |
Pact消费者测试(TypeScript)
import { PactV3, MatchersV3 } from '@pact-foundation/pact';
const provider = new PactV3({
consumer: 'OrderService',
provider: 'UserService'
});
describe('User API Contract', () => {
it('returns user by ID', async () => {
await provider
.given('user 123 exists')
.uponReceiving('a request for user 123')
.withRequest({ method: 'GET', path: '/users/123' })
.willRespondWith({
status: 200,
body: MatchersV3.like({
id: '123',
name: MatchersV3.string('John'),
email: MatchersV3.email('john@example.com')
})
})
.executeTest(async (mockServer) => {
const response = await fetch(`${mockServer.url}/users/123`);
expect(response.status).toBe(200);
});
});
it('returns 404 for non-existent user', async () => {
await provider
.given('user does not exist')
.uponReceiving('a request for non-existent user')
.withRequest({ method: 'GET', path: '/users/999' })
.willRespondWith({
status: 404,
body: MatchersV3.like({
error: { code: 'NOT_FOUND', message: MatchersV3.string() }
})
})
.executeTest(async (mockServer) => {
const response = await fetch(`${mockServer.url}/users/999`);
expect(response.status).toBe(404);
});
});
});
提供者验证
import { Verifier } from '@pact-foundation/pact';
new Verifier({
provider: 'UserService',
providerBaseUrl: 'http://localhost:3000',
pactBrokerUrl: process.env.PACT_BROKER_URL,
publishVerificationResult: true,
providerVersion: process.env.GIT_SHA,
stateHandlers: {
'user 123 exists': async () => {
await db.users.create({ id: '123', name: 'John' });
},
'user does not exist': async () => {
await db.users.deleteAll();
}
}
}).verifyProvider();
OpenAPI验证(Express)
const OpenApiValidator = require('express-openapi-validator');
app.use(OpenApiValidator.middleware({
apiSpec: './openapi.yaml',
validateRequests: true,
validateResponses: true,
validateSecurity: true
}));
其他实现
- Python JSON Schema: 参见references/python-json-schema.md
- Java REST Assured: 参见references/java-rest-assured.md
- Pact Broker CI/CD: 参见references/pact-broker-cicd.md
最佳实践
做:
- 从消费者角度测试
- 使用匹配器进行灵活匹配
- 验证结构,而非特定值
- 明确版本化契约
- 测试错误响应
- 在CI管道中运行测试
- 测试向后兼容性
不做:
- 在契约中测试业务逻辑
- 硬编码特定值
- 跳过错误场景
- 忽略版本控制
- 未经验证部署
工具
- Pact - 多语言消费者驱动的契约
- Spring Cloud Contract - JVM生态系统
- OpenAPI/Swagger - 模式优先验证
- Dredd - API蓝图测试
- Spectral - OpenAPI代码检查