渐进式编程课
鸿蒙 目录
第 41 课2026.08.27

适用场景:多个成功或失败用例共用测试结构,但事件记录、失败配置和被测对象会在运行中改变

第 41 课:Hypium 多阶段测试共享夹具与用例隔离

  • 日期:2026-08-27
  • 课程序号:第 41 课
  • 知识点:用 beforeEach 重建多阶段测试的可变夹具,保证参数化用例彼此隔离
  • 适用场景:多个成功或失败用例共用测试结构,但事件记录、失败配置和被测对象会在运行中改变

承接上一课:上一课建立了多阶段失败矩阵;本课进一步隔离每个矩阵用例的运行状态,避免前一个用例留下的事件影响后一个用例。

核心概念

  • 参数表等只读数据可以在套件内共享;事件数组、Fake 配置和被测对象属于可变夹具,应在每个用例前重建。
  • beforeEach 在每个 it 执行前运行,适合集中创建一套干净的测试环境。
  • 用例只设置自己的差异条件,例如 failAt,其余准备工作交给共享夹具。
  • 用例隔离后,测试结果不依赖执行顺序,单独运行某个用例也应得到相同结果。

最小 ArkTS / Hypium 示例

ts
import { beforeEach, describe, expect, it } from '@ohos/hypium';

interface FailureCase {
  name: string;
  failAt: string;
  expectedEvents: string;
}

class Stage {
  constructor(private readonly name: string,
    private readonly events: string[]) {}

  async run(failAt: string): Promise<void> {
    this.events.push(`${this.name}-started`);
    if (failAt === this.name) {
      throw new Error(`${this.name}失败`);
    }
    this.events.push(`${this.name}-finished`);
  }
}

class PublishController {
  constructor(private readonly events: string[]) {}

  async publish(failAt: string): Promise<void> {
    await new Stage('保存', this.events).run(failAt);
    await new Stage('校验', this.events).run(failAt);
    await new Stage('上传', this.events).run(failAt);
    this.events.push('发布成功');
  }
}

const cases: FailureCase[] = [
  {
    name: '保存失败后停止',
    failAt: '保存',
    expectedEvents: '保存-started'
  },
  {
    name: '校验失败后停止',
    failAt: '校验',
    expectedEvents: '保存-started,保存-finished,校验-started'
  }
];

export default function isolatedFailureMatrixTest(): void {
  describe('isolated failure matrix', () => {
    let events: string[] = [];
    let controller: PublishController = new PublishController(events);

    beforeEach(() => {
      events = [];
      controller = new PublishController(events);
    });

    cases.forEach((testCase: FailureCase) => {
      it(testCase.name, 0, async () => {
        await controller.publish(testCase.failAt).catch(() => {});

        expect(events.join(',')).assertEqual(testCase.expectedEvents);
      });
    });
  });
}

这里共享的是不会在测试中修改的 cases;events 和 controller 则由 beforeEach 为每个用例重新创建。

3~5 分钟练习

增加一个“全部阶段成功”的用例,并确保它可以放在失败用例之前或之后运行,事件断言都不会受其他用例影响。

参考答案

先在参数表中加入成功场景:

ts
{
  name: '全部阶段成功',
  failAt: '',
  expectedEvents: '保存-started,保存-finished,校验-started,校验-finished,' +
    '上传-started,上传-finished,发布成功'
}

beforeEach 会在该用例开始前把 events 重置为空数组,并创建引用新数组的 controller。因此无论成功用例排在矩阵中的什么位置,它都只会断言本次执行产生的事件。

与上一课的联系

上一课用参数化矩阵覆盖各阶段失败;本课保留同一矩阵结构,并用 beforeEach 隔离事件记录和被测对象,使各失败场景不受执行顺序影响。