渐进式编程课
鸿蒙 目录
第 23 课2026.08.04

适用场景:一组测试需要复用创建成本较高、且测试期间保持只读的资源

第 23 课:Hypium 套件级生命周期钩子

  • 日期:2026-08-04
  • 课程序号:第 23 课
  • 知识点:beforeAll、afterAll 与每用例钩子的区别
  • 适用场景:一组测试需要复用创建成本较高、且测试期间保持只读的资源

承接上一课:beforeEach / afterEach 为每个用例提供隔离环境;本课学习只执行一次的套件级钩子,并明确共享资源的安全边界。

核心概念

Hypium 在同一个 describe 中的大致执行顺序是:

text
beforeAll
  beforeEach -> it -> afterEach
  beforeEach -> it -> afterEach
afterAll
  • beforeAll:整组测试开始前执行一次,适合读取固定配置、构造只读测试数据。
  • afterAll:整组测试结束后执行一次,适合释放套件级资源。
  • beforeEach:每个用例前执行,适合创建带可变状态的服务实例。

不要通过 beforeAll 共享会被测试修改的对象,否则用例可能相互污染,并依赖执行顺序。

最小 ArkTS 示例

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

class MessageService {
  private readonly prefix: string;

  constructor(prefix: string) {
    this.prefix = prefix;
  }

  format(message: string): string {
    return `${this.prefix}${message}`;
  }
}

export default function messageServiceSuiteTest(): void {
  describe('MessageService suite', () => {
    let readonlyPrefix: string;
    let service: MessageService;

    beforeAll(() => {
      readonlyPrefix = '[测试]';
    });

    beforeEach(() => {
      service = new MessageService(readonlyPrefix);
    });

    afterAll(() => {
      readonlyPrefix = '';
    });

    it('formatsFirstMessage', 0, () => {
      expect(service.format('通知')).assertEqual('[测试]通知');
    });

    it('formatsSecondMessage', 0, () => {
      expect(service.format('提醒')).assertEqual('[测试]提醒');
    });
  });
}

固定前缀由 beforeAll 准备一次;服务实例仍在 beforeEach 中重新创建,因此将来服务增加可变状态时,各用例仍互不影响。

3~5 分钟练习

新增一个只读后缀 readonlySuffix:在 beforeAll 中设为 !,让 format() 返回 [测试]通知!,并修改第一条断言。

参考答案

ts
let readonlySuffix: string;

beforeAll(() => {
  readonlyPrefix = '[测试]';
  readonlySuffix = '!';
});

// 将后缀作为构造参数传入,并在 format 中拼接。
expect(service.format('通知')).assertEqual('[测试]通知!');

关键是后缀在所有用例中保持只读;如果测试会修改它,就应改为在 beforeEach 中重新准备。

与上一课的联系

上一课用 beforeEach / afterEach 隔离每个测试用例;本课补充 beforeAll / afterAll,用于整组测试共享只读资源。两者结合后,可以同时兼顾资源准备成本与用例隔离。