- Create user/ - User guides and configuration documentation - Create features/ - Feature specifications and business flows - Create debugging/ - Debug guides and quick references - Create testing/ - Test infrastructure, reports, and plans - Create internal/ - Internal plans, analyses, and templates - Move cleaner/*.md to cleaner/ directory - Move LOGGING_*.md to developer/guides/ Add docs/README.md as documentation index with category navigation and quick lookup guide. The reorganized structure makes it easier for users and developers to quickly locate relevant documentation.
198 lines
3.9 KiB
Markdown
198 lines
3.9 KiB
Markdown
# Mock Library 使用指南
|
||
|
||
ERPAuto 测试框架提供的 Mock 工厂函数,帮助你快速创建类型安全的测试替身。
|
||
|
||
## 快速开始
|
||
|
||
```typescript
|
||
import { createMockLogger, createMockConfigManager } from '@/tests/mocks'
|
||
|
||
const mockLogger = createMockLogger()
|
||
const mockConfig = createMockConfigManager({
|
||
logging: { level: 'debug', auditRetention: 30, appRetention: 14 }
|
||
})
|
||
```
|
||
|
||
## Logger Mock
|
||
|
||
```typescript
|
||
const mockLogger = createMockLogger()
|
||
mockLogger.info('test')
|
||
expect(mockLogger.info).toHaveBeenCalledWith('test')
|
||
|
||
// 预设行为
|
||
const mockLogger = createMockLogger({
|
||
error: vi.fn(() => console.log('logged'))
|
||
})
|
||
|
||
// Child logger
|
||
const child = mockLogger.child('OrderService')
|
||
```
|
||
|
||
## ConfigManager Mock
|
||
|
||
```typescript
|
||
const mockConfig = createMockConfigManager({
|
||
logging: { level: 'debug' },
|
||
erp: { url: 'https://test.local' }
|
||
})
|
||
|
||
expect(mockConfig.getConfig().logging.level).toBe('debug')
|
||
mockConfig.updateConfig.mockResolvedValue({ success: true })
|
||
```
|
||
|
||
## ERP Auth Mock
|
||
|
||
```typescript
|
||
const mockAuth = createMockErpAuthService({ isLoggedIn: true })
|
||
expect(mockAuth.isActive()).toBe(true)
|
||
|
||
mockAuth.login.mockRejectedValue(new Error('Auth failed'))
|
||
await expect(mockAuth.login()).rejects.toThrow()
|
||
```
|
||
|
||
## Playwright Mock
|
||
|
||
```typescript
|
||
const mockPage = createMockPage()
|
||
mockPage.goto.mockResolvedValue(undefined)
|
||
|
||
const mockLocator = createMockLocator()
|
||
mockLocator.fill.mockResolvedValue(undefined)
|
||
mockLocator.click.mockResolvedValue(undefined)
|
||
```
|
||
|
||
## 常见模式
|
||
|
||
### 1. Stubbing - 预设返回值
|
||
|
||
```typescript
|
||
mockConfig.getConfig.mockReturnValue({ logging: { level: 'debug' } })
|
||
mockConfig.updateConfig.mockResolvedValue({ success: true })
|
||
```
|
||
|
||
### 2. Spying - 跟踪调用
|
||
|
||
```typescript
|
||
service.doWork(mockLogger)
|
||
expect(mockLogger.info).toHaveBeenCalledWith('Work started')
|
||
```
|
||
|
||
### 3. Behavior Preset - 预设行为
|
||
|
||
```typescript
|
||
mockAuth.login.mockRejectedValue(new Error('Auth failed'))
|
||
await expect(mockAuth.login()).rejects.toThrow()
|
||
```
|
||
|
||
## 反模式
|
||
|
||
### ❌ 复杂条件逻辑
|
||
|
||
```typescript
|
||
// 错误
|
||
mockConfig.getConfig.mockImplementation(() => {
|
||
if (condition) return configA
|
||
else return configB
|
||
})
|
||
|
||
// 正确
|
||
mockConfig.getConfig.mockReturnValue(fixedConfig)
|
||
```
|
||
|
||
### ❌ 真实网络调用
|
||
|
||
```typescript
|
||
// 错误
|
||
mockPage.goto.mockImplementation(async (url) => {
|
||
await fetch(url)
|
||
})
|
||
|
||
// 正确
|
||
mockPage.goto.mockResolvedValue(undefined)
|
||
```
|
||
|
||
### ❌ 过度 Mock
|
||
|
||
```typescript
|
||
// 错误:Mock 每个方法
|
||
createMockLogger({
|
||
info: vi.fn(),
|
||
error: vi.fn(),
|
||
warn: vi.fn(),
|
||
debug: vi.fn(),
|
||
verbose: vi.fn(),
|
||
child: vi.fn()
|
||
})
|
||
|
||
// 正确:只覆盖需要的
|
||
createMockLogger()
|
||
createMockLogger({ error: vi.fn() })
|
||
```
|
||
|
||
## 迁移指南
|
||
|
||
### vi.mock() → createMockXxx()
|
||
|
||
**旧方式**:
|
||
|
||
```typescript
|
||
vi.mock('./logger', () => ({
|
||
createLogger: vi.fn(() => ({ info: vi.fn() }))
|
||
}))
|
||
```
|
||
|
||
**新方式**:
|
||
|
||
```typescript
|
||
import { createMockLogger } from '@/tests/mocks'
|
||
const logger = createMockLogger()
|
||
```
|
||
|
||
**优势**: 类型安全、预设默认值、统一维护
|
||
|
||
### 手写 Mock → 工厂函数
|
||
|
||
**旧方式**:
|
||
|
||
```typescript
|
||
const mock = { getConfig: vi.fn(), updateConfig: vi.fn() }
|
||
```
|
||
|
||
**新方式**:
|
||
|
||
```typescript
|
||
const mock = createMockConfigManager()
|
||
```
|
||
|
||
**优势**: 不遗漏方法、配置自动合并
|
||
|
||
## 最佳实践
|
||
|
||
1. 优先使用工厂函数
|
||
2. 只 Mock 依赖,不 Mock 被测试类本身
|
||
3. 保持 Mock 简单
|
||
4. 用命名和注释说明 Mock 目的
|
||
|
||
## 完整示例
|
||
|
||
```typescript
|
||
import { describe, it, expect } from 'vitest'
|
||
import { createMockLogger, createMockConfigManager } from '@/tests/mocks'
|
||
|
||
describe('OrderService', () => {
|
||
it('should process order', () => {
|
||
const logger = createMockLogger()
|
||
const config = createMockConfigManager({
|
||
extraction: { batchSize: 100 }
|
||
})
|
||
|
||
const service = new OrderService(logger, config)
|
||
service.processOrder('ORD-001')
|
||
|
||
expect(logger.info).toHaveBeenCalledWith('Processing: ORD-001')
|
||
expect(config.getConfig).toHaveBeenCalled()
|
||
})
|
||
})
|
||
```
|