Status: done
作为测试开发者, 我想要确保测试隔离和并行执行, 以便残疾人管理和区域管理测试可以同时运行。
Given 所有业务测试已完成 When 验证测试隔离和并行执行 Then 包含以下验证:
数据隔离策略
并行执行验证
测试顺序独立性
[x] Task 1: 分析当前测试的隔离情况 (AC: #1) ✅
[x] Task 2: 改进数据隔离策略 (AC: #1) ✅
[x] Task 3: 验证并行执行 (AC: #2) ✅
--workers=4 运行残疾人管理测试[x] Task 4: 验证测试顺序独立性 (AC: #3) ✅
Epic 9: 残疾人管理完整 E2E 测试覆盖(含并行隔离)
为残疾人管理功能编写完整的、真正验证业务功能的 E2E 测试,并确保测试可以与未来的区域管理测试并行运行。
Epic 9 Story 依赖关系:
问题背景:
test.describe.serial 串行执行目标:
--workers=N)--shuffle)问题 1: 共享测试数据
从 Story 9.5 的代码审查中发现,多个测试使用相同的省市选择(湖北省/武汉市),可能导致并发时的数据冲突。
问题 2: 不完整的清理
部分测试在 test.afterEach 中没有正确清理数据,导致后续测试可能受到影响。
问题 3: 串行执行限制
使用 test.describe.serial 意味着测试必须串行运行,这违背了 Playwright 并行执行的设计初衷。
策略 1: 唯一 ID 生成模式
// 在 test.describe 级别生成时间戳
test.describe('残疾人管理 - 功能测试', () => {
const TEST_TIMESTAMP = Date.now();
const TEST_PREFIX = `test_${TEST_TIMESTAMP}`;
test('测试 A', async ({ page }) => {
const uniqueName = `${TEST_PREFIX}_A`;
// 使用 uniqueName 创建数据
});
test('测试 B', async ({ page }) => {
const uniqueName = `${TEST_PREFIX}_B`;
// 使用 uniqueName 创建数据
});
});
策略 2: 测试后清理机制
test.afterEach(async ({ disabilityPersonPage }) => {
// 方案 A: 删除本次测试创建的所有数据
const testRecords = await disabilityPersonPage.searchByName(TEST_PREFIX);
for (const record of testRecords) {
await disabilityPersonPage.deleteDisabilityPerson(record.name);
}
// 方案 B: 如果后端支持事务,使用事务回滚
// await rollbackTestData(TEST_TIMESTAMP);
});
策略 3: 并行执行配置
# 移除 test.describe.serial,使用默认并行模式
cd web
pnpm test:e2e:chromium --workers=4
策略 4: 省市选择多样化
为了避免并发时的省市下拉框冲突,每个测试使用不同的省市组合:
const PROVINCE_CITY_MAP = [
{ province: '北京市', city: '北京市' },
{ province: '上海市', city: '上海市' },
{ province: '广东省', city: '广州市' },
{ province: '湖北省', city: '武汉市' },
{ province: '四川省', city: '成都市' },
];
// 基于测试索引选择不同的省市
const location = PROVINCE_CITY_MAP[testIndex % PROVINCE_CITY_MAP.length];
# playwright.config.ts 中的配置
export default defineConfig({
fullyParallel: true, // 默认 true,启用并行
workers: process.env.CI ? 1 : undefined, // CI 环境单 worker,本地环境自动检测
// 或者指定 worker 数量
workers: 4,
});
# 并行运行所有测试(默认)
cd web
pnpm test:e2e:chromium
# 指定 4 个 worker
pnpm test:e2e:chromium --workers=4
# 指定单个文件并行运行
pnpm test:e2e:chromium disability-person-photo.spec.ts --workers=2
# 随机顺序运行测试
pnpm test:e2e:chromium --shuffle
# 指定随机种子以复现问题
pnpm test:e2e:chromium --shuffle --seed=12345
Epic 9 已完成的测试文件:
| 测试文件 | 测试数量 | 使用模式 | 需要改进 |
|---|---|---|---|
disability-person-photo.spec.ts |
8 | serial | ✅ 需要改为并行 |
disability-person-bankcard.spec.ts |
5 | serial | ✅ 需要改为并行 |
disability-person-note.spec.ts |
4 | serial | ✅ 需要改为并行 |
disability-person-visit.spec.ts |
4 | serial | ✅ 需要改为并行 |
disability-person-crud.spec.ts |
16 | serial | ✅ 需要改为并行 |
改进目标:
test.describe.serialtest.afterEach 清理E2E 测试目录结构:
web/tests/e2e/
├── fixtures/ # 测试文件(图片、文档)
├── pages/ # Page Object
│ └── admin/
│ ├── disability-person.page.ts
│ └── region-management.page.ts
├── specs/ # 测试用例
│ └── admin/
│ ├── disability-person-photo.spec.ts
│ ├── disability-person-bankcard.spec.ts
│ ├── disability-person-note.spec.ts
│ ├── disability-person-visit.spec.ts
│ └── disability-person-crud.spec.ts
└── playwright.config.ts
与 Epic 8(区域管理)的关系:
Story 9.1 (照片上传功能测试) 的经验:
test.describe.serial 串行执行id-card-front.jpgStory 9.2 (银行卡管理功能测试) 的经验:
test.describe.serial 确保测试顺序Story 9.3 (备注管理功能测试) 的经验:
Story 9.4 (回访记录管理测试) 的经验:
test.describe.serialStory 9.5 (完整 CRUD 测试) 的经验:
test.describe.serial 包裹 16 个测试关键经验总结:
test.describe.serial(需要改进)Recent Commits:
02aa2b97 - test(e2e): 完成 Story 9.5 代码审查 - 残疾人管理完整 CRUD 测试e8c55856 - test(e2e): 完成 Story 9.5 - 残疾人管理完整 CRUD 测试c588c1e6 - docs(e2e): 创建 Story 10.4 - 创建订单测试测试文件模式:
disability-person-{feature}.spec.tsdata-testid 优先陷阱 1: test.describe.serial 限制并行 ⚠️
test.describe.serial 会阻止并行执行陷阱 2: 共享 fixtures 的并发访问 ⚠️
陷阱 3: 异步 Select 的并发等待 ⚠️
陷阱 4: 测试清理的竞态条件 ⚠️
test.afterEach 在并行模式下同时执行陷阱 5: 全局状态的污染 ⚠️
test.beforeEach 而不是 test.beforeAll源文档引用:
前置 Story 参考:
Playwright 文档:
Claude Opus 4 (claude-opus-4-5-20251101)
test.describe.serial,改为并行执行test.afterEach 清理机制修改的测试文件(5 个):
| 文件 | 修改内容 | 状态 |
|---|---|---|
disability-person-photo.spec.ts |
移除 serial,添加 TEST_TIMESTAMP/TEST_PREFIX,移除全局 createdTestData | ✅ 完成 |
disability-person-bankcard.spec.ts |
移除 serial,添加 TEST_TIMESTAMP/TEST_PREFIX,移除全局 createdTestData | ✅ 完成 |
disability-person-note.spec.ts |
移除 serial,添加 TEST_TIMESTAMP/TEST_PREFIX | ✅ 完成 |
disability-person-visit.spec.ts |
移除 serial,添加 TEST_TIMESTAMP/TEST_PREFIX | ✅ 完成 |
disability-person-crud.spec.ts |
移除 serial,添加统一 afterEach 清理,添加 createdTestData.push() | ✅ 完成 |
并行执行验证结果:
photo.spec.ts: 8 passed (2.2m) with 2 workers ✅
note.spec.ts: 8 passed (1.1m) with 2 workers ✅
photo.spec.ts: 8 passed (3.3m) with 1 worker ✅
photo.spec.ts: 8 passed (1.1m) with 4 workers ✅ (速度提升 3x)
关键改进:
test.describe 而不是 test.describe.serial创建的文件:
_bmad-output/implementation-artifacts/9-6-parallel-isolation.md - 本 story 文档修改的文件:
web/tests/e2e/specs/admin/disability-person-photo.spec.ts - 移除 serial,添加时间戳web/tests/e2e/specs/admin/disability-person-bankcard.spec.ts - 移除 serial,添加时间戳web/tests/e2e/specs/admin/disability-person-note.spec.ts - 移除 serial,添加时间戳web/tests/e2e/specs/admin/disability-person-visit.spec.ts - 移除 serial,添加时间戳web/tests/e2e/specs/admin/disability-person-crud.spec.ts - 移除 serial,添加 afterEach 清理更新的配置文件:
_bmad-output/implementation-artifacts/sprint-status.yaml - Story 9.6 状态更新为 done