第 32 课2026.08.17
适用场景:多个用例反复创建相同的 Fake、Spy 和被测对象,准备代码过多,真正的测试差异不突出
第 32 课:Hypium 测试数据工厂
- 日期:2026-08-17
- 课程序号:第 32 课
- 知识点:用小型工厂函数集中创建测试依赖和被测对象
- 适用场景:多个用例反复创建相同的 Fake、Spy 和被测对象,准备代码过多,真正的测试差异不突出
承接上一课:上一课把不同业务契约拆成独立用例;本课用测试数据工厂减少拆分后重复的 Arrange 代码,同时保持每个用例的差异清晰。
核心概念
- 工厂函数提供一套可用的默认测试环境,统一创建 Fake、Spy 和被测对象。
- 用例只传入与默认条件不同的依赖,例如失败用例只替换保存服务。
- 工厂只负责装配测试对象,不隐藏 Act 和 Assert;测试正文仍应明确执行了什么、验证了什么。
最小 ArkTS / Hypium 示例
ts
import { describe, expect, it } from '@ohos/hypium';
interface DraftService {
save(content: string): Promise<void>;
}
interface NoticeService {
send(message: string): void;
}
class SuccessfulDraftService implements DraftService {
save(content: string): Promise<void> {
return Promise.resolve();
}
}
class FailingDraftService implements DraftService {
save(content: string): Promise<void> {
return Promise.reject(new Error(`保存失败:${content}`));
}
}
class SpyNoticeService implements NoticeService {
callCount: number = 0;
send(message: string): void {
this.callCount += 1;
}
}
class DraftPublisher {
constructor(
private draftService: DraftService,
private noticeService: NoticeService
) {}
async publish(content: string): Promise<void> {
await this.draftService.save(content);
this.noticeService.send('发布成功');
}
}
interface PublisherFixture {
publisher: DraftPublisher;
noticeService: SpyNoticeService;
}
function createPublisherFixture(
draftService: DraftService = new SuccessfulDraftService()
): PublisherFixture {
const noticeService: SpyNoticeService = new SpyNoticeService();
return {
publisher: new DraftPublisher(draftService, noticeService),
noticeService: noticeService
};
}
export default function draftPublisherFactoryTest(): void {
describe('DraftPublisher', () => {
it('notifiesWhenSaveSucceeds', 0, async () => {
// Arrange:直接使用默认成功环境
const fixture: PublisherFixture = createPublisherFixture();
// Act
await fixture.publisher.publish('鸿蒙笔记');
// Assert
expect(fixture.noticeService.callCount).assertEqual(1);
});
it('doesNotNotifyWhenSaveFails', 0, async () => {
// Arrange:只覆盖本用例不同的失败依赖
const fixture: PublisherFixture = createPublisherFixture(
new FailingDraftService()
);
// Act
try {
await fixture.publisher.publish('鸿蒙笔记');
} catch (error) {
// 本用例只验证失败后不通知
}
// Assert
expect(fixture.noticeService.callCount).assertEqual(0);
});
});
}createPublisherFixture() 默认构造成功场景;失败用例只替换 DraftService。两个用例仍明确保留 Act 和 Assert,不把业务行为藏进工厂。
3~5 分钟练习
给 PublisherFixture 增加 draftService 字段,让用例既能取得被测对象,也能直接取得本次使用的保存服务。修改工厂返回值,但不要把 publish() 调用放进工厂。
参考答案
ts
interface PublisherFixture {
publisher: DraftPublisher;
draftService: DraftService;
noticeService: SpyNoticeService;
}
function createPublisherFixture(
draftService: DraftService = new SuccessfulDraftService()
): PublisherFixture {
const noticeService: SpyNoticeService = new SpyNoticeService();
return {
publisher: new DraftPublisher(draftService, noticeService),
draftService: draftService,
noticeService: noticeService
};
}工厂集中返回测试所需对象,但何时执行 publish()、断言哪个行为,仍由各用例决定。
与上一课的联系
上一课通过单一行为原则把契约拆开,可能带来重复的 Arrange 代码;本课用测试数据工厂集中默认装配,让用例保持独立的同时只突出各自的条件差异。