name: wms-testing-patterns description: “仓库管理系统测试模式,涵盖库存操作、拣货/包装/发货流程、波次管理、EDI X12/EDIFACT 合规性、RF/条码扫描以及 WMS-ERP 集成。适用于测试 WMS 平台(Blue Yonder、Manhattan、SAP EWM)。” category: 企业集成 priority: 高 tokenEstimate: 1800 agents: [qe-middleware-validator, qe-contract-validator, qe-sap-idoc-tester, qe-odata-contract-validator] implementation_status: 优化 optimization_version: 2.0 last_optimized: 2026-02-04 dependencies: [api-testing-patterns, contract-testing, enterprise-integration-testing] quick_reference_card: true tags: [wms, warehouse, inventory, edi, pick-pack-ship, blue-yonder, manhattan, sap-ewm, rf-scanning] trust_tier: 3 validation: schema_path: schemas/output.json validator_path: scripts/validate-config.json eval_path: evals/wms-testing-patterns.yaml
WMS 测试模式
<default_to_action> 测试仓库管理系统时:
- 验证库存准确性(收货、上架、周期盘点、调整)
- 测试拣货/包装/发货端到端流程(波次释放 -> 发货确认)
- 验证EDI消息处理(856 发货通知、940 订单、945 确认、943/944 库存)
- 断言RF/条码扫描流程(扫描 -> 验证 -> 更新 -> 确认)
- 执行分配和补货逻辑(FIFO、FEFO、批次控制)
- 测试WMS-ERP集成(库存同步、订单状态、货物收货)
- 验证波次管理(波次计划、释放、短拣处理)
快速模式选择:
- 库存差异 -> 周期盘点和调整测试
- 订单履行问题 -> 拣货/包装/发货流程测试
- EDI故障 -> 消息格式和确认测试
- 扫描问题 -> RF设备模拟测试
- 分配错误 -> 批次控制和FIFO/FEFO测试
- 集成缺口 -> WMS-ERP同步边界测试
关键成功因素:
- WMS准确性直接影响客户体验和财务报告
- 始终测试正常流程和异常流程(短拣、损坏货物、退货)
- EDI测试必须覆盖语法验证和业务规则验证 </default_to_action>
快速参考卡
何时使用
- 测试WMS平台(Blue Yonder/JDA、Manhattan Associates、SAP EWM、Oracle WMS)
- 验证库存交易准确性(收货、拣货、调整)
- 测试EDI文档交换(X12 856/940/945/943/944、EDIFACT DESADV/ORDERS)
- 验证RF/移动扫描流程
- 测试波次管理和分配逻辑
- 验证WMS到ERP集成(SAP MM/WM、Oracle)
测试级别
| 级别 | 目的 | 依赖 | 速度 |
|---|---|---|---|
| 单元逻辑 | 分配/补货规则 | 无 | 快 |
| API合同 | WMS REST/SOAP端点合同 | API桩 | 快 |
| EDI验证 | 文档语法 + 业务规则 | EDI解析器 | 中 |
| 集成 | WMS-ERP库存同步 | 完整堆栈 | 较慢 |
| 端到端流程 | 完整订单履行周期 | WMS + ERP + TMS | 慢 |
关键测试场景
| 场景 | 必须测试 | 示例 |
|---|---|---|
| 收货 | 入库准确性 | PO收货,带超/欠交货容差 |
| 上架 | 位置分配 | 基于产品属性的定向上架 |
| 拣货 | 波次/批次拣货 | 多订单波次,带优先级分配 |
| 包装 | 包装验证 | 纸箱化和重量/尺寸验证 |
| 发货 | 承运商分配 | 费率比价和标签生成 |
| 周期盘点 | 库存准确性 | 盲盘 vs. 引导盘点对账 |
| 退货 | 逆向物流 | RMA收货、检查、处置 |
| EDI 856 | ASN生成 | 发货确认触发正确的ASN给客户 |
| EDI 940 | 仓库订单 | 入库订单创建正确的工作订单 |
| 短拣 | 异常处理 | 部分分配,创建缺货订单 |
工具
- WMS平台:Blue Yonder WMS、Manhattan WMOS、SAP EWM、Oracle WMS Cloud
- EDI测试:Bots EDI、SPS Commerce Test、Cleo Clarify
- RF模拟:自定义RF模拟器、Zebra SDK测试工具
- 集成:SAP PI/PO、MuleSoft、Dell Boomi
- 监控:Splunk、WMS仪表板、EDI跟踪门户
代理协调
qe-middleware-validator:WMS-ERP集成流程和消息转换验证qe-contract-validator:EDI文档合同和WMS API合同qe-sap-idoc-tester:WMS-SAP IDoc验证(WMMBID01、SHPCON)qe-odata-contract-validator:SAP EWM OData服务测试
库存交易测试
收货和上架
describe('入库收货 - 带批次控制的PO', () => {
it('接收带有批次跟踪的货物针对PO', async () => {
const receipt = await wms.receive({
po: 'PO-4500001234',
item: 'MAT-100',
quantity: 100,
lot: 'LOT-2026-001',
expiryDate: '2027-06-30',
uom: 'EA'
});
expect(receipt.status).toBe('RECEIVED');
expect(receipt.quantityReceived).toBe(100);
expect(receipt.lot).toBe('LOT-2026-001');
// 验证上架任务已创建
const putawayTask = await wms.getTask(receipt.putawayTaskId);
expect(putawayTask.type).toBe('DIRECTED_PUTAWAY');
expect(putawayTask.suggestedLocation).toMatch(/^BIN-/);
});
it('拒绝超出容差的超收货', async () => {
// PO行是100 EA,容差是10%
const result = await wms.receive({
po: 'PO-4500001234',
item: 'MAT-100',
quantity: 115, // 15% 超,超出10%容差
lot: 'LOT-2026-002'
});
expect(result.status).toBe('REJECTED');
expect(result.reason).toContain('超收货容差超出');
expect(result.maxAllowed).toBe(110);
});
it('接受欠收货并创建剩余未清数量', async () => {
const result = await wms.receive({
po: 'PO-4500001234',
item: 'MAT-100',
quantity: 80,
lot: 'LOT-2026-003'
});
expect(result.status).toBe('PARTIAL_RECEIPT');
expect(result.quantityReceived).toBe(80);
expect(result.remainingOpen).toBe(20);
});
});
describe('定向上架', () => {
it('基于产品属性分配位置', async () => {
// 危险品应去危险品区
const putaway = await wms.executePutaway({
item: 'MAT-HAZ-001',
attributes: { hazmat: true, temperature: 'ambient' },
quantity: 50,
lot: 'LOT-HAZ-001'
});
expect(putaway.location).toMatch(/^HAZ-/); // 危险品区前缀
expect(putaway.status).toBe('PUTAWAY_COMPLETE');
});
it('主区满时回退到溢出区', async () => {
// 先填满主区
await wms.fillZone('ZONE-A', 100); // 100% 容量
const putaway = await wms.executePutaway({
item: 'MAT-200',
quantity: 10,
preferredZone: 'ZONE-A'
});
expect(putaway.location).toMatch(/^OVF-/); // 溢出区
expect(putaway.overflowReason).toBe('PRIMARY_ZONE_FULL');
});
});
周期盘点和调整
describe('周期盘点 - 盲盘', () => {
it('自动批准阈值内的差异', async () => {
// 系统显示100 EA,物理盘点是98
const count = await wms.submitCycleCount({
location: 'BIN-A-01-01',
item: 'MAT-100',
countedQuantity: 98,
countType: 'BLIND', // 盘点员看不到系统数量
varianceThreshold: 5 // 百分比
});
expect(count.systemQuantity).toBe(100);
expect(count.variance).toBe(-2);
expect(count.variancePercent).toBeCloseTo(2.0);
expect(count.autoApproved).toBe(true);
expect(count.adjustmentPosted).toBe(true);
});
it('差异超出阈值时需要主管批准', async () => {
const count = await wms.submitCycleCount({
location: 'BIN-A-01-02',
item: 'MAT-200',
countedQuantity: 80,
countType: 'BLIND',
varianceThreshold: 5 // 20% 差异超出5%阈值
});
expect(count.variancePercent).toBeCloseTo(20.0);
expect(count.autoApproved).toBe(false);
expect(count.adjustmentPosted).toBe(false);
expect(count.status).toBe('PENDING_SUPERVISOR_APPROVAL');
});
it('差异严重时触发重新盘点', async () => {
const count = await wms.submitCycleCount({
location: 'BIN-A-01-03',
item: 'MAT-300',
countedQuantity: 0, // 系统显示50
countType: 'BLIND',
varianceThreshold: 5,
criticalThreshold: 50
});
expect(count.variancePercent).toBe(100);
expect(count.status).toBe('RECOUNT_REQUIRED');
expect(count.recountAssigned).toBe(true);
expect(count.recountAssignee).not.toBe(count.originalCounter);
});
});
拣货/包装/发货流程测试
波次管理
describe('波次计划和释放', () => {
it('从符合条件的订单创建波次,使用批量拣货策略', async () => {
// 为波次计划创建5个订单
const orders = await Promise.all(
Array.from({ length: 5 }, (_, i) => wms.createOrder({
orderId: `SO-100${i}`,
lines: [
{ item: 'MAT-100', qty: 10 },
{ item: 'MAT-200', qty: 5 }
],
priority: i === 0 ? 'RUSH' : 'STANDARD',
shipBy: '2026-02-05'
}))
);
const wave = await wms.planWave({
strategy: 'BATCH_PICK',
maxOrders: 10,
cutoffTime: '2026-02-05T14:00:00Z'
});
expect(wave.orderCount).toBe(5);
expect(wave.pickTasks).toBeGreaterThan(0);
expect(wave.status).toBe('PLANNED');
// 加急订单应在拣货序列中优先
const firstTask = wave.pickTasks[0];
expect(firstTask.orderId).toBe('SO-1000');
expect(firstTask.priority).toBe('RUSH');
});
it('处理短拣,创建缺货订单', async () => {
// 仅5 EA可用,订单需要10
await wms.setInventory('BIN-B-01-01', 'MAT-500', 5);
const order = await wms.createOrder({
orderId: 'SO-SHORT',
lines: [{ item: 'MAT-500', qty: 10 }]
});
const wave = await wms.planWave({ strategy: 'SINGLE_ORDER' });
await wms.releaseWave(wave.id);
// 尝试拣货
const pickResult = await wms.confirmPick({
taskId: wave.pickTasks[0].id,
pickedQty: 5 // 仅5可用
});
expect(pickResult.status).toBe('SHORT_PICK');
expect(pickResult.pickedQuantity).toBe(5);
expect(pickResult.shortQuantity).toBe(5);
expect(pickResult.backorderCreated).toBe(true);
expect(pickResult.backorderId).toBeDefined();
});
});
分配逻辑
describe('FIFO分配', () => {
it('首先分配最旧的批次', async () => {
// 设置两个批次,收货日期不同
await wms.receiveInventory('MAT-FIFO', 50, { lot: 'LOT-OLD', receiptDate: '2026-01-01' });
await wms.receiveInventory('MAT-FIFO', 50, { lot: 'LOT-NEW', receiptDate: '2026-02-01' });
const allocation = await wms.allocate({
item: 'MAT-FIFO',
quantity: 30,
method: 'FIFO'
});
expect(allocation.allocatedLot).toBe('LOT-OLD');
expect(allocation.quantity).toBe(30);
});
it('当最旧批次不足时,跨批次拆分分配', async () => {
await wms.receiveInventory('MAT-SPLIT', 20, { lot: 'LOT-A', receiptDate: '2026-01-01' });
await wms.receiveInventory('MAT-SPLIT', 50, { lot: 'LOT-B', receiptDate: '2026-01-15' });
const allocation = await wms.allocate({
item: 'MAT-SPLIT',
quantity: 40,
method: 'FIFO'
});
expect(allocation.splits).toHaveLength(2);
expect(allocation.splits[0]).toEqual({ lot: 'LOT-A', quantity: 20 });
expect(allocation.splits[1]).toEqual({ lot: 'LOT-B', quantity: 20 });
});
});
describe('FEFO分配(先到期先出)', () => {
it('首先分配到期最早的批次', async () => {
await wms.receiveInventory('MAT-FEFO', 50, { lot: 'LOT-EXP-LATE', expiry: '2027-12-31' });
await wms.receiveInventory('MAT-FEFO', 50, { lot: 'LOT-EXP-SOON', expiry: '2026-06-30' });
const allocation = await wms.allocate({
item: 'MAT-FEFO',
quantity: 30,
method: 'FEFO'
});
expect(allocation.allocatedLot).toBe('LOT-EXP-SOON');
});
it('从分配中排除过期批次', async () => {
await wms.receiveInventory('MAT-EXPIRED', 100, { lot: 'LOT-PAST', expiry: '2025-12-31' });
await wms.receiveInventory('MAT-EXPIRED', 50, { lot: 'LOT-VALID', expiry: '2027-12-31' });
const allocation = await wms.allocate({
item: 'MAT-EXPIRED',
quantity: 30,
method: 'FEFO'
});
expect(allocation.allocatedLot).toBe('LOT-VALID');
expect(allocation.excludedLots).toContain('LOT-PAST');
expect(allocation.excludeReason).toBe('EXPIRED');
});
});
包装和发货
describe('包装验证', () => {
it('验证包装内容匹配拣货清单', async () => {
const packResult = await wms.packVerify({
orderId: 'SO-1001',
cartonId: 'CTN-001',
scannedItems: [
{ barcode: 'MAT-100-LOT001', qty: 10 },
{ barcode: 'MAT-200-LOT002', qty: 5 }
]
});
expect(packResult.status).toBe('VERIFIED');
expect(packResult.allItemsScanned).toBe(true);
expect(packResult.weightValidation).toBe('WITHIN_TOLERANCE');
});
it('拒绝缺少物品的包装', async () => {
const packResult = await wms.packVerify({
orderId: 'SO-1001',
cartonId: 'CTN-002',
scannedItems: [
{ barcode: 'MAT-100-LOT001', qty: 10 }
// 缺少 MAT-200
]
});
expect(packResult.status).toBe('INCOMPLETE');
expect(packResult.missingItems).toContainEqual({ item: 'MAT-200', expectedQty: 5 });
});
});
describe('发货和承运商分配', () => {
it('跨承运商比价并选择最便宜的', async () => {
const shipment = await wms.shipConfirm({
orderId: 'SO-1001',
cartons: ['CTN-001'],
carrierSelection: 'RATE_SHOP',
eligibleCarriers: ['UPS', 'FEDEX', 'USPS']
});
expect(shipment.status).toBe('SHIPPED');
expect(shipment.carrier).toBeDefined();
expect(shipment.trackingNumber).toBeDefined();
expect(shipment.rateShopResults).toHaveLength(3);
expect(shipment.selectedRate).toBeLessThanOrEqual(
Math.min(...shipment.rateShopResults.map(r => r.rate))
);
});
it('生成发货标签并触发EDI 856', async () => {
const shipment = await wms.shipConfirm({
orderId: 'SO-1002',
cartons: ['CTN-003'],
carrier: 'UPS',
service: 'GROUND'
});
expect(shipment.label).toBeDefined();
expect(shipment.label.format).toBe('ZPL');
expect(shipment.edi856Generated).toBe(true);
expect(shipment.edi856.transactionSetId).toMatch(/^856/);
});
});
EDI文档测试
EDI 856 - 发货通知
describe('EDI 856 ASN生成', () => {
it('生成带正确层级结构的有效856', async () => {
const asn = await edi.generate856({
shipmentId: 'SHP-001',
carrier: { scac: 'UPSN', proNumber: 'PRO-12345' },
orders: [{
orderId: 'SO-1001',
items: [
{ sku: 'MAT-100', qty: 10, lot: 'LOT-001', upc: '012345678901' }
]
}]
});
// 验证层级结构:发货 -> 订单 -> 物品
expect(asn.segments.filter(s => s.id === 'HL')).toHaveLength(3);
expect(asn.getSegment('BSN', 'BSN01')).toBe('00'); // 原始
expect(asn.getSegment('TD5', 'TD504')).toBe('UPSN');
expect(asn.getSegment('SN1', 'SN102')).toBe('10');
// 验证段数和控制号
expect(asn.getSegment('CTT', 'CTT01')).toBe('1'); // 1个事务
expect(asn.getSegment('SE', 'SE01')).toBeDefined(); // 段数
});
it('验证已发送856的997功能确认', async () => {
const asn = await edi.send856('SHP-001');
const ack = await edi.receive997({ timeout: 30000 });
expect(ack.transactionSetId).toBe('997');
expect(ack.acknowledgmentCode).toBe('A'); // 接受
expect(ack.referencedTransactionSet).toBe('856');
});
it('处理997拒绝并触发重发', async () => {
const asn = await edi.send856('SHP-INVALID');
const ack = await edi.receive997({ timeout: 30000 });
expect(ack.acknowledgmentCode).toBe('R'); // 拒绝
expect(ack.errors).toContainEqual(
expect.objectContaining({ segmentId: 'SN1', errorCode: '7' })
);
// 验证重发已触发
const resendLog = await edi.getResendLog('SHP-INVALID');
expect(resendLog.attempts).toBeGreaterThanOrEqual(1);
});
});
EDI 940 - 仓库发货订单
describe('EDI 940 入库处理', () => {
it('从有效940文档创建WMS订单', async () => {
const edi940 = loadEdiDocument('testdata/edi-940-standard.edi');
const result = await edi.process940(edi940);
expect(result.orderCreated).toBe(true);
expect(result.wmsOrderId).toBeDefined();
expect(result.lineCount).toBe(5);
expect(result.allocationTriggered).toBe(true);
expect(result.priorityMapped).toBe('STANDARD');
});
it('拒绝带未知SKU的940并生成997拒绝', async () => {
const edi940 = loadEdiDocument('testdata/edi-940-invalid-sku.edi');
const result = await edi.process940(edi940);
expect(result.orderCreated).toBe(false);
expect(result.errors).toContainEqual(
expect.objectContaining({ segment: 'LIN', error: '未知 SKU: INVALID-SKU' })
);
expect(result.ack997.code).toBe('R');
});
it('通过BSN参考号检测重复940', async () => {
const edi940 = loadEdiDocument('testdata/edi-940-standard.edi');
// 首次处理 - 成功
await edi.process940(edi940);
// 处理重复 - 应检测
const result = await edi.process940(edi940);
expect(result.orderCreated).toBe(false);
expect(result.duplicate).toBe(true);
expect(result.originalOrderId).toBeDefined();
});
});
EDI 945 - 仓库发货建议
describe('EDI 945 出库生成', () => {
it('发货确认时生成945', async () => {
await wms.shipConfirm({ orderId: 'SO-1005', carrier: 'FEDEX' });
const edi945 = await edi.getLatest945('SO-1005');
expect(edi945.transactionSetId).toBe('945');
expect(edi945.getSegment('W06', 'W0601')).toBe('N'); // 正常发货
expect(edi945.getSegment('W12', 'W1202')).toBeDefined(); // 发货数量
expect(edi945.getSegment('N1', 'N102')).toBeDefined(); // 收货方名称
});
});
RF/条码扫描流程测试
describe('RF定向拣货流程', () => {
let rfSession;
beforeEach(async () => {
rfSession = await rfSimulator.createSession({
device: 'Zebra-MC9300',
mode: 'directed-pick'
});
});
it('用有效扫描完成定向拣货', async () => {
// 步骤1:登录
const login = await rfSession.scan('badge', 'USER-PICKER-01');
expect(login.screen).toBe('MAIN_MENU');
// 步骤2:选择拣货任务
const taskSelect = await rfSession.selectMenu('PICK');
expect(taskSelect.screen).toBe('PICK_TASK_ASSIGNED');
expect(taskSelect.taskId).toBeDefined();
// 步骤3:扫描源位置
const locationScan = await rfSession.scan('location', 'BIN-A-01-01');
expect(locationScan.screen).toBe('SCAN_ITEM');
expect(locationScan.expectedItem).toBe('MAT-100');
// 步骤4:扫描物品条码
const itemScan = await rfSession.scan('item', 'MAT-100-LOT001');
expect(itemScan.screen).toBe('ENTER_QUANTITY');
expect(itemScan.maxQty).toBe(10);
// 步骤5:输入数量
const qtyConfirm = await rfSession.enterQuantity(10);
expect(qtyConfirm.screen).toBe('SCAN_DESTINATION');
// 步骤6:扫描暂存通道
const destScan = await rfSession.scan('location', 'STAGE-LANE-01');
expect(destScan.screen).toBe('PICK_COMPLETE');
expect(destScan.taskStatus).toBe('COMPLETED');
});
it('用不匹配警报拒绝错误物品扫描', async () => {
await rfSession.scan('badge', 'USER-PICKER-01');
await rfSession.selectMenu('PICK');
await rfSession.scan('location', 'BIN-A-01-01');
const wrongItem = await rfSession.scan('item', 'MAT-999-WRONG');
expect(wrongItem.screen).toBe('ITEM_MISMATCH');
expect(wrongItem.alert).toBe('MISMATCH');
expect(wrongItem.expectedItem).toBe('MAT-100');
expect(wrongItem.scannedItem).toBe('MAT-999');
});
it('超拣数量时警告', async () => {
await rfSession.scan('badge', 'USER-PICKER-01');
await rfSession.selectMenu('PICK');
await rfSession.scan('location', 'BIN-A-01-01');
await rfSession.scan('item', 'MAT-100-LOT001');
const overPick = await rfSession.enterQuantity(999);
expect(overPick.screen).toBe('OVER_PICK_WARNING');
expect(overPick.maxAllowed).toBe(10);
expect(overPick.requiresSupervisor).toBe(true);
});
it('优雅处理错误位置扫描', async () => {
await rfSession.scan('badge', 'USER-PICKER-01');
await rfSession.selectMenu('PICK');
const wrongLocation = await rfSession.scan('location', 'BIN-Z-99-99');
expect(wrongLocation.screen).toBe('WRONG_LOCATION');
expect(wrongLocation.expectedLocation).toBe('BIN-A-01-01');
expect(wrongLocation.action).toBe('RESCAN');
});
});
describe('RF收货流程', () => {
it('通过扫描PO和物品收货', async () => {
const rfSession = await rfSimulator.createSession({ mode: 'receiving' });
await rfSession.scan('badge', 'USER-RECEIVER-01');
const poScan = await rfSession.scan('document', 'PO-4500001234');
expect(poScan.screen).toBe('SCAN_ITEM');
expect(poScan.expectedItems).toHaveLength(3);
const itemScan = await rfSession.scan('item', 'MAT-100');
expect(itemScan.screen).toBe('ENTER_QUANTITY');
const receipt = await rfSession.enterQuantity(100);
expect(receipt.screen).toBe('SCAN_LOT');
const lotScan = await rfSession.scan('lot', 'LOT-2026-050');
expect(lotScan.screen).toBe('RECEIPT_CONFIRMED');
expect(lotScan.quantityReceived).toBe(100);
});
});
WMS-ERP集成测试
SAP EWM 到 S/4HANA 同步
describe('WMS-ERP货物收货同步', () => {
it('在SLA内同步货物收货从EWM到S/4HANA', async () => {
const startTime = Date.now();
// 在EWM中确认入库交货
await ewm.confirmInboundDelivery({
deliveryId: 'IBD-001',
items: [{ material: 'MAT-100', quantity: 100, batch: 'BATCH-001' }]
});
// 等待同步到S/4HANA
const materialDoc = await s4hana.waitForMaterialDocument({
reference: 'IBD-001',
timeout: 30000
});
const elapsed = Date.now() - startTime;
expect(materialDoc.created).toBe(true);
expect(materialDoc.type).toBe('WE'); // 货物收货
expect(materialDoc.quantity).toBe(100);
expect(materialDoc.batch).toBe('BATCH-001');
expect(materialDoc.plant).toBe('1000');
expect(elapsed).toBeLessThan(30000); // 30s SLA
});
it('验证EWM和S/4HANA之间的库存数量匹配', async () => {
const ewmStock = await ewm.getStock('MAT-100', 'WH-01');
const s4Stock = await s4hana.getStock('MAT-100', '1000', '0001');
expect(ewmStock.available).toBe(s4Stock.unrestricted);
expect(ewmStock.allocated).toBe(s4Stock.reserved);
expect(ewmStock.blocked).toBe(s4Stock.qualityInspection);
});
it('处理同步失败,带IDoc错误和重试', async () => {
// 模拟S/4HANA不可用
await s4hana.simulateDowntime(10000);
await ewm.confirmInboundDelivery({
deliveryId: 'IBD-FAIL',
items: [{ material: 'MAT-200', quantity: 50 }]
});
// 检查IDoc状态
const idoc = await sap.getIdoc({ reference: 'IBD-FAIL', type: 'WMMBID01' });
expect(idoc.status).toBe('03'); // 错误状态
// 恢复S/4HANA并验证自动重试
await s4hana.restoreService();
const retried = await sap.waitForIdocStatus(idoc.id, '53', { timeout: 60000 });
expect(retried.status).toBe('53'); // 成功处理
});
});
Manhattan WMOS 到 Oracle EBS 同步
describe('Manhattan WMOS 到 Oracle EBS', () => {
it('通过REST API向Oracle发布库存调整', async () => {
const adjustment = await wmos.postAdjustment({
item: 'ITEM-500',
location: 'LOC-A-01',
adjustmentQty: -5,
reason: 'DAMAGED'
});
const oracleTransaction = await oracle.waitForTransaction({
reference: adjustment.transactionId,
timeout: 15000
});
expect(oracleTransaction.transactionType).toBe('ADJUSTMENT');
expect(oracleTransaction.quantity).toBe(-5);
expect(oracleTransaction.reasonCode).toBe('DAMAGED');
expect(oracleTransaction.accountPosted).toBe(true);
});
});
退货和逆向物流
describe('退货处理', () => {
it('处理RMA收货,带检查和处置', async () => {
const rma = await wms.createRMA({
rmaNumber: 'RMA-001',
originalOrder: 'SO-1001',
items: [{ sku: 'MAT-100', qty: 2, reason: 'DEFECTIVE' }]
});
// 接收退货
const receipt = await wms.receiveReturn({
rmaId: rma.id,
scannedItems: [{ barcode: 'MAT-100', qty: 2 }]
});
expect(receipt.status).toBe('RECEIVED_PENDING_INSPECTION');
// 检查退货物品
const inspection = await wms.inspectReturn({
rmaId: rma.id,
results: [
{ item: 'MAT-100', unit: 1, condition: 'RESTOCK', grade: 'A' },
{ item: 'MAT-100', unit: 2, condition: 'SCRAP', grade: 'F' }
]
});
expect(inspection.disposition.restock).toBe(1);
expect(inspection.disposition.scrap).toBe(1);
expect(inspection.restockLocation).toBeDefined();
});
});
补货测试
describe('补货触发器', () => {
it('当拣选面低于最小值时触发最小/最大补货', async () => {
// 设置拣选面到最小阈值
await wms.setInventory('PICK-A-01', 'MAT-100', 5); // 最小 = 10, 最大 = 50
const replenishment = await wms.checkReplenishment('PICK-A-01', 'MAT-100');
expect(replenishment.triggered).toBe(true);
expect(replenishment.type).toBe('MIN_MAX');
expect(replenishment.replenishQty).toBe(45); // 填充到最大 (50 - 5)
expect(replenishment.sourceLocation).toMatch(/^BULK-/);
});
it('从波次分配触发基于需求的补货', async () => {
// 波次需要100 EA,拣选面仅有20
await wms.setInventory('PICK-B-01', 'MAT-200', 20);
const wave = await wms.planWave({
strategy: 'SINGLE_ORDER',
orders: [{ orderId: 'SO-REPLEN', lines: [{ item: 'MAT-200', qty: 100 }] }]
});
const replenishment = await wms.getReplenishmentTasks(wave.id);
expect(replenishment).toHaveLength(1);
expect(replenishment[0].type).toBe('DEMAND');
expect(replenishment[0].quantity).toBe(80); // 100 需要 - 20 可用
expect(replenishment[0].priority).toBe('URGENT');
});
});
最佳实践
要这样做
- 测试完整生命周期:收货 -> 上架 -> 分配 -> 拣货 -> 包装 -> 发货 -> EDI
- 在每个状态转换时验证库存准确性(在手、已分配、在途)
- 测试所有WMS分配方法(FIFO、FEFO、LIFO、区优先级)
- 模拟RF设备故障在事务中(电池耗尽、WiFi中断、扫描超时)
- 验证每个出库文档的EDI确认(997)
- 显式测试短拣、超拣和错误拣货场景
- 验证WMS-ERP同步延迟相对于SLA阈值
- 测试多个阈值的周期盘点差异(自动批准、主管、重新盘点)
避免这样
- 仅测试快乐路径收货,无超/欠容差场景
- 跳过RF错误流程(错误扫描、超时、连接丢失)
- 忽略EDI段级验证(仅信任文档级通过)
- 测试分配逻辑无过期批次场景
- 假设WMS-ERP同步是瞬时的;始终用定时断言测试
- 部署前未测试混合优先级订单的波次计划
- 忽略包装验证中的纸箱化和重量验证
代理辅助WMS测试
// 验证EDI文档合同
await Task("EDI合同验证", {
documents: ['856-ASN', '940-Order', '945-Advice', '943-StockTransfer'],
validateSegments: true,
checkAcknowledgments: true,
testErrorScenarios: ['invalid-sku', 'duplicate-bsn', 'missing-required']
}, "qe-contract-validator");
// 测试WMS-ERP集成流程
await Task("WMS-ERP集成测试", {
source: 'SAP-EWM',
target: 'SAP-S4HANA',
flows: ['goods-receipt', 'goods-issue', 'stock-transfer', 'inventory-adjustment'],
slaThreshold: 30000,
validateIdocs: true
}, "qe-middleware-validator");
// 验证SAP IDoc结构
await Task("IDoc结构验证", {
idocTypes: ['WMMBID01', 'SHPCON', 'MBGMCR01'],
validateSegments: true,
testMandatoryFields: true,
crossReferenceWithEWM: true
}, "qe-sap-idoc-tester");
// 测试EWM OData服务
await Task("EWM OData服务测试", {
services: [
'/sap/opu/odata/sap/API_WAREHOUSE_ORDER',
'/sap/opu/odata/sap/API_PHYSICAL_INVENTORY_DOC'
],
testCrud: true,
validateNavigationProperties: true,
testFilterAndPagination: true
}, "qe-odata-contract-validator");
代理协调提示
主要代理
- qe-middleware-validator:WMS-ERP集成流程测试和消息转换验证
- qe-contract-validator:EDI文档合同测试和WMS API合同
支持代理
- qe-message-broker-tester:当WMS使用消息队列进行异步处理时
- qe-odata-contract-validator:当WMS暴露OData服务时(SAP EWM)
- qe-sap-idoc-tester:当WMS-SAP集成使用IDocs时(WMMBID01、SHPCON)
协调模式
1. qe-contract-validator -> 验证EDI文档模式和段
2. qe-middleware-validator -> 测试WMS消息流程和转换
3. qe-message-broker-tester -> 验证异步队列处理和DLQ
4. qe-odata-contract-validator -> 测试WMS OData API(如果SAP EWM)
5. qe-sap-idoc-tester -> 验证IDoc结构和内容
内存命名空间
aqe/wms-testing/
inventory/ - 收货、上架、调整测试结果
fulfillment/ - 拣货/包装/发货流程结果
edi/ - EDI文档验证结果
rf-scanning/ - RF流程模拟结果
allocation/ - FIFO/FEFO/批次控制测试结果
integration/ - WMS-ERP同步测试结果
returns/ - RMA和逆向物流结果
车队协调
const wmsFleet = await FleetManager.coordinate({
strategy: 'wms-testing',
agents: [
'qe-contract-validator', // EDI合同和API合同
'qe-middleware-validator', // WMS-ERP消息流程
'qe-sap-idoc-tester', // IDoc验证
'qe-odata-contract-validator' // SAP EWM OData测试
],
topology: 'mesh'
});
await wmsFleet.execute({
workflows: [
{ name: 'inbound', stages: ['receive', 'putaway', 'inventory-sync'] },
{ name: 'outbound', stages: ['wave', 'pick', 'pack', 'ship', 'edi-856'] },
{ name: 'returns', stages: ['rma-receipt', 'inspection', 'disposition'] }
],
testEdi: true,
testRfScanning: true,
testAllocation: true
});
QCSD集成
- 构思:标记
HAS_WMS触发WMS特定质量标准(库存准确性、EDI合规性) - 精化:SFDIPOT 包括仓库操作产品因素(吞吐量、准确性、延迟)
- 开发:覆盖分析包括EDI文档解析路径和分配算法
- 验证:流水线门包括WMS集成健康检查和EDI确认验证
相关技能
- api-testing-patterns - REST/GraphQL API测试基础
- contract-testing - 消费者驱动合同测试与Pact
- enterprise-integration-testing - 编排所有企业代理
- middleware-testing-patterns - ESB和消息代理测试
- observability-testing-patterns - 监控和警报验证
记住
仓库管理系统是物理数字桥梁。当库存错误时,客户收到错误发货,财务报告不准确,补货失败。测试每个库存状态转换、每个EDI文档字段和每个RF扫描异常。2%的库存差异看似小,但在50个仓库的100,000个SKU中会累积放大。
使用代理: 代理段对段验证EDI文档结构,在SLA阈值内测试WMS-ERP同步,并大规模模拟RF扫描流程。使用代理覆盖分配方法、批次控制场景和EDI错误条件的组合爆炸,这是手动测试无法达到的。