关于 middleware-testing-patterns
middleware-testing-patterns 是用于测试企业中间件、ESB 和消息驱动系统的技能,提供路由、转换、错误队列、顺序、重试、协议中介及集成模式的测试场景与实践。
主要功能
- 01验证消息路由
- 02端到端转换测试
- 03死信队列与重试检查
- 04消息顺序与关联 ID 检查
- 05错误处理与补偿测试
- 06协议中介与 EIP 测试
适用场景
- 01测试消息代理路由和死信队列
- 02检查 JSON、XML 和定长文件转换
- 03用关联 ID 跟踪多跳工作流
- 04验证 SOAP 到 REST 中介及重试
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ proffesor-for-testing/ agentic-qe --skill middleware-testing-patterns安装后在 Claude Code 中输入/middleware-testing-patterns确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
evals/middleware-testing-patterns.yamlschemas/output.jsonscripts/validate-config.json详细介绍
中间件测试模式
<default_to_action> 测试企业中间件、ESB 或消息驱动系统时:
- 验证消息路由逻辑(基于内容、基于标头、收件人列表)
- 端到端测试消息转换(输入格式 -> 输出格式)
- 验证死信队列处理(有毒消息、重试耗尽)
- 使用关联 ID 断言消息顺序和序列
- 演练错误处理和补偿模式
- 测试协议中介(SOAP 到 REST、同步到异步)
- 验证 EIP 模式(拆分器、聚合器、内容增强器、规范化器)
快速模式选择:
- 消息路由问题 -> 基于内容的路由器测试
- 转换失败 -> 输入架构/输出架构验证
- 消息丢失 -> DLQ 和重试模式测试
- 协议桥接 -> 中介往返测试
- 复杂流程 -> 关联 ID 跟踪测试
关键成功因素:
- 中间件正常工作时是不可见的;要测试这些不可见之处
- 务必同时验证正常路径和错误通道(DLQ)
- 关联 ID 是跟踪多跳消息的生命线 </default_to_action>
快速参考卡
适用场景
- 测试 ESB 消息流(IBM IIB/ACE、MuleSoft、WSO2)
- 验证消息转换(XSLT、JSON 到 XML、平面文件解析)
- 测试消息代理路由(MQ、Kafka、RabbitMQ)
- 验证死信队列行为
- 测试协议中介(SOAP/REST 桥接)
- 验证企业集成模式(EIP)
测试级别
| 级别 | 目的 | 依赖项 | 速度 |
|---|---|---|---|
| 单元转换 | 单个映射的正确性 | 无 | 快 |
| 路由逻辑 | 路由决策的准确性 | 模拟端点 | 快 |
| 集成 | 端到端消息流 | 代理 + 端点 | 中等 |
| DLQ/错误 | 错误处理和恢复 | 完整中间件堆栈 | 较慢 |
关键测试场景
| 场景 | 必测项 | 示例 |
|---|---|---|
| 路由 | 正确选择目标 | 订单类型 A -> 队列 A,类型 B -> 队列 B |
| 转换 | 映射后的架构合规性 | XML -> JSON 字段映射准确性 |
| DLQ | 有毒消息处理 | 格式错误的 XML 进入 DLQ,而非丢失 |
| 顺序 | 保持序列 | 消息 1-2-3 按顺序到达 |
| 关联 | 多跳跟踪 | 请求-回复通过关联 ID 匹配 |
| 重试 | 瞬时故障恢复 | 退避重试 3 次,然后进入 DLQ |
| 中介 | 协议桥接保真度 | SOAP 请求生成正确的 REST 调用 |
工具
- 消息代理:IBM MQ、RabbitMQ、Apache Kafka、ActiveMQ
- ESB:IBM IIB/ACE、MuleSoft、WSO2、Apache Camel
- 测试:SoapUI、Postman、自定义测试工具
- 虚拟化:Mountebank、WireMock、HoverFly
- 监控:Splunk、ELK、Datadog
Agent 协作
qe-middleware-validator:验证路由规则、转换准确性和 EIP 模式qe-message-broker-tester:测试代理连接、DLQ 行为和消息顺序qe-soap-tester:SOAP/WSDL 验证、WS-Security、协议中介
消息路由模式测试
基于内容的路由器
describe('Content-Based Router - Order Type', () => {
it('routes standard orders to fulfillment queue', async () => {
const message = {
correlationId: uuid(),
body: { orderType: 'STANDARD', orderId: 'ORD-001', items: [{ sku: 'A1', qty: 2 }] }
};
await broker.publish('orders.inbound', message);
const routed = await broker.consume('orders.fulfillment', { timeout: 5000 });
expect(routed.body.orderId).toBe('ORD-001');
expect(routed.correlationId).toBe(message.correlationId);
const dlq = await broker.tryConsume('orders.dlq', { timeout: 1000 });
expect(dlq).toBeNull(); // Nothing in DLQ
});
it('routes express orders to priority queue', async () => {
const message = {
correlationId: uuid(),
body: { orderType: 'EXPRESS', orderId: 'ORD-002', items: [{ sku: 'B1', qty: 1 }] }
};
await broker.publish('orders.inbound', message);
const routed = await broker.consume('orders.priority', { timeout: 5000 });
expect(routed.body.orderId).toBe('ORD-002');
});
it('sends unrecognized order types to DLQ', async () => {
const message = {
correlationId: uuid(),
body: { orderType: 'UNKNOWN', orderId: 'ORD-003' }
};
await broker.publish('orders.inbound', message);
const dlqMessage = await broker.consume('orders.dlq', { timeout: 5000 });
expect(dlqMessage.body.orderId).toBe('ORD-003');
expect(dlqMessage.headers['x-error-reason']).toContain('Unrecognized orderType');
});
});
基于标头的路由器
describe('Header-Based Router - Region', () => {
it('routes by x-region header to regional queues', async () => {
const regions = ['US', 'EU', 'APAC'];
for (const region of regions) {
const message = {
headers: { 'x-region': region },
body: { orderId: `ORD-${region}` }
};
await broker.publish('orders.global', message);
const routed = await broker.consume(`orders.${region.toLowerCase()}`, { timeout: 5000 });
expect(routed.body.orderId).toBe(`ORD-${region}`);
}
});
it('routes messages without region header to default queue', async () => {
await broker.publish('orders.global', { body: { orderId: 'ORD-NO-REGION' } });
const routed = await broker.consume('orders.default', { timeout: 5000 });
expect(routed.body.orderId).toBe('ORD-NO-REGION');
});
});
收件人列表
describe('Recipient List - Order Notifications', () => {
it('fans out order to all registered recipients', async () => {
const message = {
body: { orderId: 'ORD-100', total: 250.00 }
};
await broker.publish('orders.confirmed', message);
// All three systems should receive the message
const [warehouse, billing, crm] = await Promise.all([
broker.consume('notify.warehouse', { timeout: 5000 }),
broker.consume('notify.billing', { timeout: 5000 }),
broker.consume('notify.crm', { timeout: 5000 })
]);
expect(warehouse.body.orderId).toBe('ORD-100');
expect(billing.body.orderId).toBe('ORD-100');
expect(crm.body.orderId).toBe('ORD-100');
});
});
消息转换测试
JSON 到 XML 转换
describe('JSON to XML Transformation', () => {
it('transforms order JSON to SAP XML format', async () => {
const jsonInput = {
orderId: 'ORD-500',
customer: { id: 'CUST-01', name: 'Acme Corp' },
items: [
{ sku: 'MAT-100', quantity: 10, unitPrice: 25.00 }
]
};
await broker.publish('transform.json-in', { body: jsonInput });
const xmlOutput = await broker.consume('transform.xml-out', { timeout: 5000 });
const parsed = parseXml(xmlOutput.body);
expect(parsed.SalesOrder.OrderNumber).toBe('ORD-500');
expect(parsed.SalesOrder.SoldToParty.CustomerID).toBe('CUST-01');
expect(parsed.SalesOrder.Items.Item[0].MaterialNumber).toBe('MAT-100');
expect(parsed.SalesOrder.Items.Item[0].Quantity).toBe('10');
});
it('handles missing optional fields gracefully', async () => {
const jsonInput = {
orderId: 'ORD-501',
customer: { id: 'CUST-02' }, // name is missing
items: [{ sku: 'MAT-200', quantity: 5 }] // unitPrice missing
};
await broker.publish('transform.json-in', { body: jsonInput });
const xmlOutput = await broker.consume('transform.xml-out', { timeout: 5000 });
const parsed = parseXml(xmlOutput.body);
expect(parsed.SalesOrder.SoldToParty.Name).toBe(''); // Default empty
expect(parsed.SalesOrder.Items.Item[0].UnitPrice).toBe('0.00');
});
it('rejects invalid input and sends to DLQ with error details', async () => {
const invalidInput = { items: [] }; // Missing required orderId and customer
await broker.publish('transform.json-in', { body: invalidInput });
const dlq = await broker.consume('transform.dlq', { timeout: 5000 });
expect(dlq.headers['x-error-reason']).toContain('orderId is required');
});
});
平面文件到 XML 转换
describe('Flat-File to XML Transformation', () => {
it('parses fixed-width flat file into structured XML', async () => {
// Fixed-width: positions 1-10 = orderId, 11-20 = sku, 21-25 = qty
const flatFile = 'ORD-001 MAT-10000010\nORD-001 MAT-20000005\n';
await broker.publish('transform.flatfile-in', { body: flatFile });
const xmlOutput = await broker.consume('transform.flatfile-xml-out', { timeout: 5000 });
const parsed = parseXml(xmlOutput.body);
expect(parsed.Orders.Order).toHaveLength(1);
expect(parsed.Orders.Order[0].Items.Item).toHaveLength(2);
expect(parsed.Orders.Order[0].Items.Item[0].Quantity).toBe('10');
});
it('handles lines with trailing whitespace correctly', async () => {
const flatFile = 'ORD-002 MAT-30000001 \n';
await broker.publish('transform.flatfile-in', { body: flatFile });
const xmlOutput = await broker.consume('transform.flatfile-xml-out', { timeout: 5000 });
const parsed = parseXml(xmlOutput.body);
expect(parsed.Orders.Order[0].OrderId).toBe('ORD-002');
expect(parsed.Orders.Order[0].Items.Item[0].Quantity).toBe('1');
});
});
死信队列(DLQ)测试
describe('Dead Letter Queue Strategy', () => {
it('moves message to DLQ after max retries', async () => {
// Configure endpoint to always fail
mockEndpoint.respondWith(500, 'Service Unavailable');
await broker.publish('orders.process', {
body: { orderId: 'ORD-FAIL' },
headers: { 'x-correlation-id': 'corr-123' }
});
// Wait for retries to exhaust (3 retries with backoff)
const dlqMessage = await broker.consume('orders.dlq', { timeout: 30000 });
expect(dlqMessage.body.orderId).toBe('ORD-FAIL');
expect(dlqMessage.headers['x-retry-count']).toBe('3');
expect(dlqMessage.headers['x-original-queue']).toBe('orders.process');
expect(dlqMessage.headers['x-correlation-id']).toBe('corr-123');
expect(dlqMessage.headers['x-error-reason']).toContain('Service Unavailable');
expect(dlqMessage.headers['x-first-failure-timestamp']).toBeDefined();
});
it('preserves original message body in DLQ', async () => {
const originalBody = {
orderId: 'ORD-PRESERVE',
items: [{ sku: 'A', qty: 1 }, { sku: 'B', qty: 2 }]
};
mockEndpoint.respondWith(500, 'Timeout');
await broker.publish('orders.process', { body: originalBody });
const dlqMessage = await broker.consume('orders.dlq', { timeout: 30000 });
expect(dlqMessage.body).toEqual(originalBody); // Body unchanged
});
it('DLQ messages can be replayed after fix', async () => {
// Step 1: Message fails, lands in DLQ
mockEndpoint.respondWith(500, 'Down');
await broker.publish('orders.process', { body: { orderId: 'ORD-REPLAY' } });
const dlqMessage = await broker.consume('orders.dlq', { timeout: 30000 });
// Step 2: Fix the endpoint
mockEndpoint.respondWith(200, 'OK');
// Step 3: Replay from DLQ
await broker.publish('orders.process', dlqMessage);
const success = await broker.consume('orders.completed', { timeout: 5000 });
expect(success.body.orderId).toBe('ORD-REPLAY');
});
});
消息顺序和序列
describe('Message Ordering Validation', () => {
it('preserves FIFO order within same partition key', async () => {
const messages = Array.from({ length: 10 }, (_, i) => ({
headers: { 'x-partition-key': 'ORDER-GROUP-1', 'x-sequence': String(i + 1) },
body: { orderId: `ORD-${i + 1}`, sequence: i + 1 }
}));
for (const msg of messages) {
await broker.publish('orders.sequenced', msg);
}
const received = [];
for (let i = 0; i < 10; i++) {
const msg = await broker.consume('orders.sequenced.out', { timeout: 5000 });
received.push(msg.body.sequence);
}
expect(received).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
});
it('detects out-of-order messages via sequence gap', async () => {
// Send messages 1, 2, 4 (skip 3)
for (const seq of [1, 2, 4]) {
await broker.publish('orders.sequenced', {
headers: { 'x-partition-key': 'GROUP-2', 'x-sequence': String(seq) },
body: { sequence: seq }
});
}
// The middleware should detect the gap and raise an alert
const alert = await broker.consume('orders.sequence-gap', { timeout: 10000 });
expect(alert.body.missingSequence).toBe(3);
expect(alert.body.partitionKey).toBe('GROUP-2');
});
});
ESB 错误处理和补偿
describe('Compensation Pattern - Saga', () => {
it('compensates completed steps when a later step fails', async () => {
// Saga: Reserve Inventory -> Charge Payment -> Ship
// If Ship fails, Payment and Inventory must be compensated
mockServices.inventory.respondWith(200); // Step 1 succeeds
mockServices.payment.respondWith(200); // Step 2 succeeds
mockServices.shipping.respondWith(500); // Step 3 fails
await broker.publish('saga.order', {
body: { orderId: 'ORD-SAGA', amount: 100.00 }
});
// Verify compensation messages were sent
const paymentRefund = await broker.consume('saga.compensate.payment', { timeout: 10000 });
expect(paymentRefund.body.action).toBe('REFUND');
expect(paymentRefund.body.orderId).toBe('ORD-SAGA');
const inventoryRelease = await broker.consume('saga.compensate.inventory', { timeout: 10000 });
expect(inventoryRelease.body.action).toBe('RELEASE');
expect(inventoryRelease.body.orderId).toBe('ORD-SAGA');
});
});
协议中介测试
SOAP 到 REST 中介
describe('SOAP to REST Protocol Mediation', () => {
it('converts SOAP request to REST call and back', async () => {
const soapRequest = `
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ord="http://example.com/orders">
<soapenv:Body>
<ord:GetOrder>
<ord:OrderId>ORD-700</ord:OrderId>
</ord:GetOrder>
</soapenv:Body>
</soapenv:Envelope>
`;
// ESB should convert SOAP -> REST GET /orders/ORD-700
mockRestBackend.onGet('/orders/ORD-700').reply(200, {
orderId: 'ORD-700',
status: 'SHIPPED',
total: 150.00
});
const response = await soapClient.send(soapRequest, { endpoint: esbSoapEndpoint });
// Verify SOAP response wraps REST data correctly
const parsed = parseSoapResponse(response);
expect(parsed.OrderId).toBe('ORD-700');
expect(parsed.Status).toBe('SHIPPED');
expect(parsed.Total).toBe('150.00');
});
it('maps REST error to SOAP fault', async () => {
const soapRequest = buildSoapGetOrder('ORD-NOTFOUND');
mockRestBackend.onGet('/orders/ORD-NOTFOUND').reply(404, {
error: 'Order not found'
});
const response = await soapClient.send(soapRequest, { endpoint: esbSoapEndpoint });
const fault = parseSoapFault(response);
expect(fault.faultCode).toBe('Client');
expect(fault.faultString).toContain('Order not found');
});
});
同步到异步中介
describe('Sync to Async Mediation', () => {
it('accepts sync request, returns correlation ID, delivers result async', async () => {
// Client sends synchronous HTTP POST
const response = await httpClient.post(`${esbEndpoint}/async-orders`, {
orderId: 'ORD-ASYNC',
items: [{ sku: 'X1', qty: 5 }]
});
// ESB returns 202 Accepted with correlation ID
expect(response.status).toBe(202);
expect(response.body.correlationId).toBeDefined();
const correlationId = response.body.correlationId;
// Result arrives asynchronously on callback queue
const result = await broker.consume('orders.async.results', {
timeout: 15000,
filter: (msg) => msg.headers['x-correlation-id'] === correlationId
});
expect(result.body.orderId).toBe('ORD-ASYNC');
expect(result.body.status).toBe('ACCEPTED');
});
});
企业集成模式(EIP)
拆分器模式
describe('Splitter - Batch Order to Individual Items', () => {
it('splits batch order into individual item messages', async () => {
const batchOrder = {
orderId: 'BATCH-001',
items: [
{ sku: 'A1', qty: 10 },
{ sku: 'B2', qty: 5 },
{ sku: 'C3', qty: 20 }
]
};
await broker.publish('orders.batch', { body: batchOrder });
const splitMessages = [];
for (let i = 0; i < 3; i++) {
splitMessages.push(await broker.consume('orders.items', { timeout: 5000 }));
}
expect(splitMessages).toHaveLength(3);
expect(splitMessages.map(m => m.body.sku).sort()).toEqual(['A1', 'B2', 'C3']);
// Each split message retains parent correlation
for (const msg of splitMessages) {
expect(msg.headers['x-batch-id']).toBe('BATCH-001');
expect(msg.headers['x-split-total']).toBe('3');
}
});
});
聚合器模式
describe('Aggregator - Collect Item Results', () => {
it('aggregates individual item results into batch response', async () => {
const batchId = 'BATCH-002';
const itemResults = [
{ sku: 'A1', status: 'PICKED', batchId },
{ sku: 'B2', status: 'PICKED', batchId },
{ sku: 'C3', status: 'PICKED', batchId }
];
for (const result of itemResults) {
await broker.publish('items.completed', {
headers: { 'x-batch-id': batchId, 'x-split-total': '3' },
body: result
});
}
const aggregated = await broker.consume('orders.batch.completed', { timeout: 10000 });
expect(aggregated.body.batchId).toBe('BATCH-002');
expect(aggregated.body.items).toHaveLength(3);
expect(aggregated.body.items.every(i => i.status === 'PICKED')).toBe(true);
});
it('handles aggregation timeout for incomplete batches', async () => {
const batchId = 'BATCH-TIMEOUT';
// Only send 2 of 3 expected items
for (let i = 0; i < 2; i++) {
await broker.publish('items.completed', {
headers: { 'x-batch-id': batchId, 'x-split-total': '3' },
body: { sku: `ITEM-${i}`, status: 'PICKED', batchId }
});
}
// After timeout, aggregator should emit partial result with error
const partial = await broker.consume('orders.batch.incomplete', { timeout: 60000 });
expect(partial.body.batchId).toBe('BATCH-TIMEOUT');
expect(partial.body.receivedCount).toBe(2);
expect(partial.body.expectedCount).toBe(3);
});
});
内容增强器模式
describe('Content Enricher - Customer Data Lookup', () => {
it('enriches order with customer details from CRM', async () => {
mockCrm.onGet('/customers/CUST-50').reply(200, {
name: 'Acme Corp',
tier: 'GOLD',
creditLimit: 50000
});
await broker.publish('orders.enrich', {
body: { orderId: 'ORD-ENRICH', customerId: 'CUST-50', total: 1200 }
});
const enriched = await broker.consume('orders.enriched', { timeout: 5000 });
expect(enriched.body.orderId).toBe('ORD-ENRICH');
expect(enriched.body.customer.name).toBe('Acme Corp');
expect(enriched.body.customer.tier).toBe('GOLD');
expect(enriched.body.creditCheckPassed).toBe(true); // 1200 < 50000
});
});
重试和断路器模式
describe('Retry with Exponential Backoff', () => {
it('retries 3 times with increasing delays before DLQ', async () => {
const timestamps = [];
mockEndpoint.onPost('/process').reply(() => {
timestamps.push(Date.now());
return [500, 'Temporary failure'];
});
await broker.publish('orders.process', { body: { orderId: 'ORD-RETRY' } });
await broker.consume('orders.dlq', { timeout: 60000 });
// Verify exponential backoff timing (1s, 2s, 4s approximately)
expect(timestamps).toHaveLength(4); // 1 initial + 3 retries
const delay1 = timestamps[1] - timestamps[0];
const delay2 = timestamps[2] - timestamps[1];
expect(delay1).toBeGreaterThan(800); // ~1s
expect(delay2).toBeGreaterThan(1800); // ~2s
});
});
describe('Circuit Breaker', () => {
it('opens circuit after threshold failures', async () => {
// Fail 5 messages to trip circuit breaker (threshold = 5)
mockEndpoint.onPost('/process').reply(500, 'Down');
for (let i = 0; i < 5; i++) {
await broker.publish('orders.circuit', { body: { orderId: `ORD-CB-${i}` } });
}
// Wait for circuit to open
await sleep(5000);
// Next message should be immediately rejected (circuit open)
const startTime = Date.now();
await broker.publish('orders.circuit', { body: { orderId: 'ORD-CB-FAST-FAIL' } });
const dlq = await broker.consume('orders.circuit.dlq', { timeout: 3000 });
const elapsed = Date.now() - startTime;
expect(dlq.headers['x-circuit-state']).toBe('OPEN');
expect(elapsed).toBeLessThan(2000); // Fast fail, no retry
});
});
用于隔离测试的服务虚拟化
describe('Service Virtualization - Middleware Isolation', () => {
let virtualService;
beforeAll(async () => {
virtualService = await mountebank.createImposter({
port: 4545,
protocol: 'http',
stubs: [
{
predicates: [{ equals: { method: 'POST', path: '/sap/orders' } }],
responses: [{
is: {
statusCode: 200,
headers: { 'Content-Type': 'application/xml' },
body: '<OrderResponse><Status>CREATED</Status><SapOrderId>SAP-001</SapOrderId></OrderResponse>'
}
}]
},
{
predicates: [{ equals: { method: 'POST', path: '/sap/orders' },
contains: { body: '<InvalidField>' } }],
responses: [{
is: { statusCode: 400, body: '<Error>Invalid request</Error>' }
}]
}
]
});
});
afterAll(async () => {
await mountebank.deleteImposter(4545);
});
it('middleware correctly handles SAP success response', async () => {
await broker.publish('orders.to-sap', {
body: { orderId: 'ORD-VIRT', customer: 'CUST-01' }
});
const result = await broker.consume('orders.sap-confirmed', { timeout: 5000 });
expect(result.body.sapOrderId).toBe('SAP-001');
});
});
最佳实践
要做
- 对每个流程同时测试正常路径和错误通道(DLQ)
- 使用关联 ID 跟踪消息在多跳中间件中的流转
- 在每个转换边界验证架构合规性
- 使用分区队列/有序队列测试消息顺序
- 使用服务虚拟化将中间件与后端系统隔离
- 测试重试耗尽,确认 DLQ 行为
- 验证多步骤流程的补偿模式(Saga 回滚)
避免
- 只测试正常路径,忽略 DLQ/错误队列
- 对队列名称进行硬编码;使用配置驱动的测试
- 忽略消息顺序要求
- 跳过转换边缘情况(空字段、特殊字符、编码)
- 测试中间件时不验证关联 ID
- 未验证退避时间,就假设重试正常工作
- 未测试熔断器阈值就进行部署
Agent 辅助的中间件测试
// Validate middleware routing rules
await Task("Middleware Routing Validation", {
routingConfig: 'esb/routing-rules.xml',
messageTypes: ['STANDARD_ORDER', 'EXPRESS_ORDER', 'RETURN'],
validateDLQ: true,
checkCorrelationIds: true
}, "qe-middleware-validator");
// Test message broker behavior
await Task("Message Broker DLQ Test", {
broker: 'rabbitmq',
queues: ['orders.inbound', 'orders.process', 'orders.dlq'],
retryPolicy: { maxRetries: 3, backoff: 'exponential' },
poisonMessages: ['malformed-xml', 'missing-required-fields', 'oversized-payload']
}, "qe-message-broker-tester");
// SOAP/WSDL contract validation
await Task("SOAP Service Contract Validation", {
wsdl: 'services/OrderService.wsdl',
endpoints: ['CreateOrder', 'GetOrderStatus', 'CancelOrder'],
validateWsSecurity: true,
testFaultHandling: true
}, "qe-soap-tester");
// Transformation accuracy testing
await Task("Transformation Accuracy Suite", {
transformations: [
{ name: 'JSON-to-SAP-XML', input: 'testdata/order.json', expected: 'testdata/order-sap.xml' },
{ name: 'Flat-to-XML', input: 'testdata/inventory.dat', expected: 'testdata/inventory.xml' }
],
validateSchemas: true,
edgeCases: ['empty-fields', 'special-chars', 'max-length']
}, "qe-middleware-validator");
Agent 协调提示
内存命名空间
aqe/middleware-testing/
routing/ - Routing rule configurations and test results
transformations/ - Mapping definitions and validation results
dlq/ - DLQ test results and replay outcomes
eip-patterns/ - EIP pattern test coverage
circuit-breakers/ - Circuit breaker state and threshold configs
correlation/ - Correlation ID tracing results
集群协调
const middlewareFleet = await FleetManager.coordinate({
strategy: 'middleware-testing',
agents: [
'qe-middleware-validator', // Routing, transformation, EIP patterns
'qe-message-broker-tester', // DLQ, ordering, retry, circuit breaker
'qe-soap-tester' // SOAP contracts, WS-Security, mediation
],
topology: 'mesh'
});
await middlewareFleet.execute({
flows: [
{ name: 'order-inbound', stages: ['route', 'transform', 'enrich', 'deliver'] },
{ name: 'payment-processing', stages: ['validate', 'authorize', 'settle'] }
],
testDLQ: true,
testCompensation: true
});
相关技能
- api-testing-patterns - REST/GraphQL API 测试基础
- contract-testing - 使用 Pact 进行消费者驱动的契约测试
- enterprise-integration-testing - 协调所有企业级 Agent
- chaos-engineering-resilience - 故障注入和韧性测试
- wms-testing-patterns - 仓储系统集成测试
请记住
中间件是企业架构的神经系统。它正常工作时,无人察觉;一旦失效,一切都会崩溃。测试路由决策、转换、错误通道和补偿模式。始终使用关联 ID 验证多跳流程,并始终确认毒消息会进入 DLQ,且包含足够的元数据以便诊断和重放。
**使用 Agent:**Agent 会根据消息类型验证路由规则,测试符合架构要求的转换准确性,并系统地测试 DLQ/重试/熔断器行为。使用 Agent 覆盖组合数量庞大的路由路径和转换边缘情况,这些情况是手动测试无法触及的。
评论
支持 Markdown