渐进式编程课
鸿蒙 目录
第 22 课2026.08.03

适用场景:一组测试需要重复创建服务对象,或每个用例结束后都要恢复测试环境

第 22 课:Hypium 测试生命周期钩子

  • 日期:2026-08-03
  • 课程序号:第 22 课
  • 知识点:beforeEach、afterEach、测试前置准备与清理
  • 适用场景:一组测试需要重复创建服务对象,或每个用例结束后都要恢复测试环境

承接上一课的异常路径测试:上一课分别创建服务并验证 Promise 成功、失败行为;本课把重复的测试准备和清理集中到生命周期钩子中,让每个用例只描述自身要验证的行为。

核心概念

Hypium 会围绕每个 it 执行对应钩子:

text
beforeEach -> it 测试用例 -> afterEach
beforeEach -> 下一个 it -> afterEach
  • beforeEach:每个测试开始前执行,适合创建全新的服务对象,避免用例之间共享可变状态。
  • afterEach:每个测试结束后执行,适合释放监听、定时器或其他测试资源。

钩子负责准备环境,不应代替断言;真正的行为验证仍写在 it 中。

最小 ArkTS 示例

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

interface MessageService {
  queryMessages(): Promise<string[]>;
}

class LocalMessageService implements MessageService {
  queryMessages(): Promise<string[]> {
    return Promise.resolve(['系统通知', '学习提醒']);
  }
}

export default function messageServiceLifecycleTest(): void {
  describe('MessageService lifecycle', () => {
    let service: MessageService;

    beforeEach(() => {
      service = new LocalMessageService();
    });

    afterEach(() => {
      // 若测试创建了监听或定时器,应在这里释放。
    });

    it('returnsTwoMessages', 0, async () => {
      const messages: string[] = await service.queryMessages();

      expect(messages.length).assertEqual(2);
    });

    it('returnsSystemMessageFirst', 0, async () => {
      const messages: string[] = await service.queryMessages();

      expect(messages[0]).assertEqual('系统通知');
    });
  });
}

两个用例都使用 beforeEach 创建的全新实例。这样即使服务以后增加内部状态,一个用例的执行结果也不会污染另一个用例。

3~5 分钟练习

在示例中增加一个 createdCount 计数器:

  1. 在 beforeEach 中创建服务后将其加 1。
  2. 新增一个测试,断言当前至少执行过一次创建。
  3. 思考为什么服务对象仍应每次重新创建,而不是只在 describe 中创建一次。

参考答案

ts
let service: MessageService;
let createdCount: number = 0;

beforeEach(() => {
  service = new LocalMessageService();
  createdCount += 1;
});

it('createsServiceBeforeTest', 0, () => {
  expect(createdCount > 0).assertTrue();
});

每次重新创建服务能隔离可变状态,使测试结果不依赖用例执行顺序。实际项目中,比起断言钩子本身,更重要的是用钩子准备环境、在 it 中断言业务行为。

与上一课的联系

上一课用可控失败实现验证异常路径,测试中需要自行准备服务对象;本课用 beforeEach 统一创建每个用例所需的独立对象,并用 afterEach 预留成对清理资源的位置。下一步可以学习 beforeAll / afterAll 与每用例钩子的差异,理解何时可以安全共享昂贵但只读的测试资源。