渐进式编程课
鸿蒙 目录
第 33 课2026.08.18

适用场景:多组输入遵循同一条业务规则,但希望每组数据都有独立用例名,失败时可直接定位具体边界场景

第 33 课:Hypium 参数化测试工厂

  • 日期:2026-08-18
  • 课程序号:第 33 课
  • 知识点:用测试用例工厂为每组边界数据注册独立的 it
  • 适用场景:多组输入遵循同一条业务规则,但希望每组数据都有独立用例名,失败时可直接定位具体边界场景

承接上一课:上一课用对象工厂集中装配 Fake、Spy 和被测对象;本课把“工厂”思想用于测试用例注册,为每组参数生成一个清晰、独立的行为化用例。

核心概念

  • 测试数据只描述用例名、输入和期望结果,不承载业务实现。
  • 用例工厂在 describe 中逐项调用 it,让每组参数成为独立测试,而不是在一个 it 内循环断言。
  • 用例名应包含边界场景,例如 returnsEmptyForZero,这样失败报告能直接指出被破坏的规则。
  • 只把 Arrange 的参数差异交给工厂;Act 和 Assert 仍在生成的用例体内清晰可见。

最小 ArkTS / Hypium 示例

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

interface UnreadCountCase {
  name: string;
  input: number;
  expected: string;
}

function formatUnreadCount(count: number): string {
  if (count <= 0) {
    return '';
  }
  if (count > 99) {
    return '99+';
  }
  return `${count}`;
}

function registerUnreadCountCase(testCase: UnreadCountCase): void {
  it(testCase.name, 0, () => {
    // Act
    const actual: string = formatUnreadCount(testCase.input);

    // Assert
    expect(actual).assertEqual(testCase.expected);
  });
}

export default function unreadCountParameterizedTest(): void {
  describe('formatUnreadCount', () => {
    const cases: UnreadCountCase[] = [
      { name: 'returnsEmptyForZero', input: 0, expected: '' },
      { name: 'returnsCountAtUpperBoundary', input: 99, expected: '99' },
      { name: 'returns99PlusWhenOverLimit', input: 100, expected: '99+' }
    ];

    cases.forEach((testCase: UnreadCountCase) => {
      registerUnreadCountCase(testCase);
    });
  });
}

这里会注册三个独立用例。任何一组失败时,报告会显示对应的行为化名称;其他参数用例仍保持独立。

3~5 分钟练习

给 cases 增加两个独立边界用例:输入 -1 时返回空字符串,输入 1 时返回 1。用行为化名称描述结果,不修改 formatUnreadCount()。

参考答案

ts
const cases: UnreadCountCase[] = [
  { name: 'returnsEmptyForNegativeCount', input: -1, expected: '' },
  { name: 'returnsEmptyForZero', input: 0, expected: '' },
  { name: 'returnsOneForSingleUnreadItem', input: 1, expected: '1' },
  { name: 'returnsCountAtUpperBoundary', input: 99, expected: '99' },
  { name: 'returns99PlusWhenOverLimit', input: 100, expected: '99+' }
];

现有业务实现已经覆盖这两个边界,无需修改生产代码;重新运行测试即可看到五个独立用例。

与上一课的联系

上一课的对象工厂减少依赖装配的重复代码;本课的用例工厂减少相同测试结构的重复代码。两者都只抽取稳定结构,不隐藏每个用例的差异条件和行为契约。