渐进式编程课
鸿蒙 目录
第 21 课2026.07.31

适用场景:数据服务可能请求失败,需要确认失败会按接口语义向调用方抛出,而不是被服务或 UI 静默吞掉

第 21 课:Hypium 异常路径测试

  • 日期:2026-07-31
  • 课程序号:第 21 课
  • 知识点:可控失败实现、Promise 拒绝、异步异常断言
  • 适用场景:数据服务可能请求失败,需要确认失败会按接口语义向调用方抛出,而不是被服务或 UI 静默吞掉

承接上一课的数据服务单元测试:上一课验证成功结果和空结果;本课补上失败路径,确认服务拒绝 Promise 时,调用方能够感知异常。

核心概念

异常路径测试需要一个结果可控的失败实现:

text
FailingMessageService
  -> queryMessages()
  -> Promise.reject(...)
  -> 测试捕获异常并断言确实发生

测试重点是可观察的服务契约,而不是页面提示文案。失败服务只用于测试,能够稳定复现错误,避免依赖真实网络是否刚好断开。

最小 ArkTS 示例

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

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

class FailingMessageService implements MessageService {
  queryMessages(): Promise<string[]> {
    return Promise.reject(new Error('network unavailable'));
  }
}

export default function messageServiceFailureTest(): void {
  describe('MessageService failure', () => {
    it('rejectsWhenQueryFails', 0, async () => {
      const service: MessageService = new FailingMessageService();
      let rejected: boolean = false;

      try {
        await service.queryMessages();
      } catch (error) {
        rejected = true;
      }

      expect(rejected).assertTrue();
    });
  });
}

await 遇到被拒绝的 Promise 会进入 catch。若服务意外吞掉错误并返回空数组,rejected 会保持 false,测试就会失败,从而暴露服务契约已经改变。

3~5 分钟练习

新增一个 SuccessfulMessageService,让它返回 ['系统通知'];复用相同的 try/catch 结构,断言调用成功时 rejected 为 false。

参考答案

ts
class SuccessfulMessageService implements MessageService {
  queryMessages(): Promise<string[]> {
    return Promise.resolve(['系统通知']);
  }
}

it('doesNotRejectWhenQuerySucceeds', 0, async () => {
  const service: MessageService = new SuccessfulMessageService();
  let rejected: boolean = false;

  try {
    await service.queryMessages();
  } catch (error) {
    rejected = true;
  }

  expect(rejected).assertFalse();
});

与上一课的联系

上一课验证 MessageService 在成功和空数据场景下遵守返回契约;本课验证失败会通过 Promise 拒绝继续向上传递。至此,数据服务的主要成功与失败分支都有了可重复执行的证据,下一步可以学习使用测试生命周期钩子减少重复的测试准备代码。