第 35 课2026.08.20
适用场景:同一个异步接口会因多种非法输入而失败,需要确认每种输入都以约定的错误信息拒绝
第 35 课:Hypium 异步失败参数化测试
- 日期:2026-08-20
- 课程序号:第 35 课
- 知识点:为每组非法参数注册独立用例,并显式验证 Promise 拒绝结果
- 适用场景:同一个异步接口会因多种非法输入而失败,需要确认每种输入都以约定的错误信息拒绝
承接上一课:上一课为多组成功参数生成独立的异步用例;本课沿用同一结构覆盖失败参数,并防止“Promise 没有拒绝但测试仍通过”的假阳性。
核心概念
- 每组非法参数仍注册为独立的
it,让失败报告直接显示具体输入场景。 - 用
then的成功和失败回调把 Promise 结果转换为明确的数据,避免异常分支被遗漏。 - 断言既要验证
rejected为true,也要验证错误信息符合契约。 - 参数表只描述输入和预期错误,不在测试中复制生产代码的判断逻辑。
最小 ArkTS / Hypium 示例
ts
import { describe, expect, it } from '@ohos/hypium';
interface RejectCase {
name: string;
userId: number;
expectedMessage: string;
}
interface PromiseResult {
rejected: boolean;
message: string;
}
class UserService {
async loadName(userId: number): Promise<string> {
if (userId <= 0) {
return Promise.reject(new Error('userId 必须大于 0'));
}
return Promise.resolve(`用户${userId}`);
}
}
function registerRejectCase(testCase: RejectCase): void {
it(testCase.name, 0, async () => {
// Arrange
const service: UserService = new UserService();
// Act
const result: PromiseResult = await service.loadName(testCase.userId).then(
(): PromiseResult => ({ rejected: false, message: '' }),
(error: Error): PromiseResult => ({ rejected: true, message: error.message })
);
// Assert
expect(result.rejected).assertTrue();
expect(result.message).assertEqual(testCase.expectedMessage);
});
}
export default function userServiceRejectParameterizedTest(): void {
describe('UserService.loadName rejects invalid ids', () => {
const cases: RejectCase[] = [
{ name: 'rejectsZeroUserId', userId: 0, expectedMessage: 'userId 必须大于 0' },
{ name: 'rejectsNegativeUserId', userId: -1, expectedMessage: 'userId 必须大于 0' }
];
cases.forEach((testCase: RejectCase) => {
registerRejectCase(testCase);
});
});
}如果被测 Promise 意外成功,result.rejected 会是 false,第一条断言立即失败;因此测试不会因为只检查错误文案而误通过。
3~5 分钟练习
给参数表增加 userId: -99 的独立失败用例,名称为 rejectsLargeNegativeUserId,预期错误信息仍为 userId 必须大于 0。不要修改 UserService 或合并已有用例。
参考答案
ts
const cases: RejectCase[] = [
{ name: 'rejectsZeroUserId', userId: 0, expectedMessage: 'userId 必须大于 0' },
{ name: 'rejectsNegativeUserId', userId: -1, expectedMessage: 'userId 必须大于 0' },
{ name: 'rejectsLargeNegativeUserId', userId: -99, expectedMessage: 'userId 必须大于 0' }
];重新运行测试后应看到三个独立失败场景用例;每个用例都会明确证明 Promise 被拒绝,并核对拒绝原因。
与上一课的联系
上一课用 async/await 验证多组 Promise 成功结果;本课继续使用“一组参数对应一个用例”的结构,把覆盖范围扩展到 Promise 拒绝路径,并增加防止假阳性的拒绝标记断言。