|
|
@@ -3,7 +3,7 @@
|
|
|
## 版本信息
|
|
|
| 版本 | 日期 | 描述 | 作者 |
|
|
|
|------|------|------|------|
|
|
|
-| 2.9 | 2025-12-15 | 添加API模拟规范和前端组件测试策略 | James |
|
|
|
+| 2.9 | 2025-12-15 | 添加API模拟规范和前端组件测试策略,修正$path()方法描述与实际代码不一致问题 | James |
|
|
|
| 2.8 | 2025-11-11 | 更新包测试结构,添加模块化包测试策略 | Winston |
|
|
|
| 2.7 | 2025-11-09 | 更新为monorepo测试架构,清理重复测试文件 | James |
|
|
|
| 2.6 | 2025-10-15 | 完成遗留测试文件迁移到统一的tests目录结构 | Winston |
|
|
|
@@ -260,45 +260,26 @@ import type { Hono } from 'hono'
|
|
|
|
|
|
// 创建模拟的rpcClient函数
|
|
|
const mockRpcClient = vi.fn((aptBaseUrl: string) => {
|
|
|
- // 创建模拟的Hono客户端结构
|
|
|
- const mockClient = {
|
|
|
- // 支持动态路径访问
|
|
|
- [Symbol.toPrimitive]: () => mockClient,
|
|
|
-
|
|
|
- // 通用API端点模拟
|
|
|
+ // 根据页面组件实际调用的RPC路径定义模拟端点
|
|
|
+ return {
|
|
|
+ // 收货地址UI包使用的端点
|
|
|
index: {
|
|
|
$get: vi.fn(),
|
|
|
$post: vi.fn(),
|
|
|
- $put: vi.fn(),
|
|
|
- $delete: vi.fn(),
|
|
|
},
|
|
|
':id': {
|
|
|
- $get: vi.fn(),
|
|
|
$put: vi.fn(),
|
|
|
$delete: vi.fn(),
|
|
|
},
|
|
|
|
|
|
- // 支持嵌套路径访问
|
|
|
- $path: (path: string) => {
|
|
|
- // 根据路径返回对应的模拟端点
|
|
|
- const pathSegments = path.split('/').filter(Boolean)
|
|
|
- let current = mockClient
|
|
|
- for (const segment of pathSegments) {
|
|
|
- if (!current[segment]) {
|
|
|
- current[segment] = {
|
|
|
- $get: vi.fn(),
|
|
|
- $post: vi.fn(),
|
|
|
- $put: vi.fn(),
|
|
|
- $delete: vi.fn(),
|
|
|
- }
|
|
|
- }
|
|
|
- current = current[segment]
|
|
|
- }
|
|
|
- return current
|
|
|
- }
|
|
|
- }
|
|
|
+ // 区域管理UI包使用的端点(跨包集成)
|
|
|
+ provinces: {
|
|
|
+ $get: vi.fn(),
|
|
|
+ },
|
|
|
|
|
|
- return mockClient
|
|
|
+ // 地区列表API端点
|
|
|
+ $get: vi.fn(),
|
|
|
+ }
|
|
|
})
|
|
|
|
|
|
// 模拟共享UI组件包中的rpcClient函数
|
|
|
@@ -383,7 +364,7 @@ describe('收货地址管理(跨UI包集成)', () => {
|
|
|
mockClient[':id']['$delete'].mockResolvedValue(createMockResponse(204));
|
|
|
|
|
|
// 配置区域API响应(区域管理UI包 - 跨包支持)
|
|
|
- mockClient.$path('api/areas').$get.mockResolvedValue(createMockResponse(200, {
|
|
|
+ mockClient.$get.mockResolvedValue(createMockResponse(200, {
|
|
|
data: [
|
|
|
{ id: 1, name: '北京市', code: '110000', level: 1 },
|
|
|
{ id: 2, name: '朝阳区', code: '110105', level: 2, parentId: 1 },
|
|
|
@@ -391,19 +372,26 @@ describe('收货地址管理(跨UI包集成)', () => {
|
|
|
]
|
|
|
}));
|
|
|
|
|
|
- mockClient.$path('api/areas/provinces').$get.mockResolvedValue(createMockResponse(200, {
|
|
|
+ mockClient.provinces.$get.mockResolvedValue(createMockResponse(200, {
|
|
|
data: [
|
|
|
{ id: 1, name: '北京市', code: '110000' },
|
|
|
{ id: 2, name: '上海市', code: '310000' }
|
|
|
]
|
|
|
}));
|
|
|
|
|
|
- mockClient.$path('api/areas/:id/cities').$get.mockResolvedValue(createMockResponse(200, {
|
|
|
- data: [
|
|
|
- { id: 2, name: '朝阳区', code: '110105', parentId: 1 },
|
|
|
- { id: 3, name: '海淀区', code: '110108', parentId: 1 }
|
|
|
- ]
|
|
|
- }));
|
|
|
+ // 获取某个地区的子地区(如城市)通常通过查询参数实现
|
|
|
+ mockClient.$get.mockImplementation((options?: any) => {
|
|
|
+ if (options?.query?.parentId === 1) {
|
|
|
+ return Promise.resolve(createMockResponse(200, {
|
|
|
+ data: [
|
|
|
+ { id: 2, name: '朝阳区', code: '110105', parentId: 1 },
|
|
|
+ { id: 3, name: '海淀区', code: '110108', parentId: 1 }
|
|
|
+ ]
|
|
|
+ }));
|
|
|
+ }
|
|
|
+ // 默认返回空列表
|
|
|
+ return Promise.resolve(createMockResponse(200, { data: [] }));
|
|
|
+ });
|
|
|
});
|
|
|
|
|
|
it('应该显示收货地址列表并支持区域选择', async () => {
|
|
|
@@ -425,7 +413,7 @@ describe('收货地址管理(跨UI包集成)', () => {
|
|
|
#### 1. 模拟范围
|
|
|
- **统一模拟点**: 集中模拟`@d8d/shared-ui-components/utils/hc`中的`rpcClient`函数
|
|
|
- **HTTP方法**: 支持Hono风格的`$get`、`$post`、`$put`、`$delete`方法
|
|
|
-- **API端点**: 支持标准端点(`index`)、参数化端点(`:id`)和嵌套路径(`$path()`)
|
|
|
+- **API端点**: 支持标准端点(`index`)、参数化端点(`:id`)和属性访问端点(如`client.provinces.$get()`)
|
|
|
- **响应格式**: 模拟完整的Response对象,包含`status`、`ok`、`json()`等方法
|
|
|
- **跨包支持**: 天然支持多个UI包组件的API模拟,无需分别模拟客户端管理器
|
|
|
|
|
|
@@ -438,6 +426,7 @@ describe('收货地址管理(跨UI包集成)', () => {
|
|
|
|
|
|
#### 3. 最佳实践
|
|
|
- **统一模拟**: 所有API调用都通过模拟`rpcClient`函数统一拦截
|
|
|
+- **按需定义**: 根据页面组件实际调用的RPC路径定义模拟端点,无需动态创建所有可能端点
|
|
|
- **类型安全**: 使用TypeScript确保模拟响应与API类型兼容
|
|
|
- **可维护性**: 保持模拟响应与实际API响应结构一致,便于后续更新
|
|
|
- **文档化**: 在测试注释中说明模拟的API行为和预期结果
|
|
|
@@ -462,13 +451,13 @@ describe('API调用验证(统一模拟)', () => {
|
|
|
mockClient.index.$get.mockResolvedValue(createMockResponse(200, { data: [] }));
|
|
|
mockClient.index.$post.mockResolvedValue(createMockResponse(201, { id: 1 }));
|
|
|
mockClient[':id']['$put'].mockResolvedValue(createMockResponse(200));
|
|
|
- mockClient.$path('api/areas').$get.mockResolvedValue(createMockResponse(200, { data: [] }));
|
|
|
+ mockClient.$get.mockResolvedValue(createMockResponse(200, { data: [] }));
|
|
|
|
|
|
// 执行测试代码(触发API调用)...
|
|
|
|
|
|
// 验证API调用次数
|
|
|
expect(mockClient.index.$get).toHaveBeenCalledTimes(1);
|
|
|
- expect(mockClient.$path('api/areas').$get).toHaveBeenCalledTimes(1);
|
|
|
+ expect(mockClient.$get).toHaveBeenCalledTimes(1);
|
|
|
|
|
|
// 验证API调用参数
|
|
|
expect(mockClient.index.$post).toHaveBeenCalledWith({
|
|
|
@@ -487,8 +476,8 @@ describe('API调用验证(统一模拟)', () => {
|
|
|
}
|
|
|
});
|
|
|
|
|
|
- // 验证嵌套路径API调用
|
|
|
- expect(mockClient.$path('api/areas').$get).toHaveBeenCalledWith({
|
|
|
+ // 验证地区API调用
|
|
|
+ expect(mockClient.$get).toHaveBeenCalledWith({
|
|
|
query: { level: 1 }
|
|
|
});
|
|
|
});
|