274 lines
6.4 KiB
Markdown
274 lines
6.4 KiB
Markdown
# Normalizer Tests
|
|
|
|
每个 normalizer 模块都有完整的测试覆盖。
|
|
|
|
## 测试结构
|
|
|
|
```
|
|
tests/normalize/normalizers/
|
|
├── __init__.py
|
|
├── test_invoice_number_normalizer.py # InvoiceNumberNormalizer 测试 (12 个测试)
|
|
├── test_ocr_normalizer.py # OCRNormalizer 测试 (9 个测试)
|
|
├── test_bankgiro_normalizer.py # BankgiroNormalizer 测试 (11 个测试)
|
|
├── test_plusgiro_normalizer.py # PlusgiroNormalizer 测试 (10 个测试)
|
|
├── test_amount_normalizer.py # AmountNormalizer 测试 (15 个测试)
|
|
├── test_date_normalizer.py # DateNormalizer 测试 (19 个测试)
|
|
├── test_organisation_number_normalizer.py # OrganisationNumberNormalizer 测试 (11 个测试)
|
|
├── test_supplier_accounts_normalizer.py # SupplierAccountsNormalizer 测试 (13 个测试)
|
|
├── test_customer_number_normalizer.py # CustomerNumberNormalizer 测试 (12 个测试)
|
|
└── README.md # 本文件
|
|
```
|
|
|
|
## 运行测试
|
|
|
|
### 运行所有 normalizer 测试
|
|
|
|
```bash
|
|
# 在 WSL 环境中
|
|
conda activate invoice-py311
|
|
pytest tests/normalize/normalizers/ -v
|
|
```
|
|
|
|
### 运行单个 normalizer 的测试
|
|
|
|
```bash
|
|
# 测试 InvoiceNumberNormalizer
|
|
pytest tests/normalize/normalizers/test_invoice_number_normalizer.py -v
|
|
|
|
# 测试 AmountNormalizer
|
|
pytest tests/normalize/normalizers/test_amount_normalizer.py -v
|
|
|
|
# 测试 DateNormalizer
|
|
pytest tests/normalize/normalizers/test_date_normalizer.py -v
|
|
```
|
|
|
|
### 查看测试覆盖率
|
|
|
|
```bash
|
|
pytest tests/normalize/normalizers/ --cov=src/normalize/normalizers --cov-report=html
|
|
```
|
|
|
|
## 测试统计
|
|
|
|
**总计**: 112 个测试用例
|
|
**状态**: ✅ 全部通过
|
|
**执行时间**: ~5.6 秒
|
|
|
|
### 各 Normalizer 测试数量
|
|
|
|
| Normalizer | 测试数量 | 覆盖率 |
|
|
|------------|---------|-------|
|
|
| InvoiceNumberNormalizer | 12 | 100% |
|
|
| OCRNormalizer | 9 | 100% |
|
|
| BankgiroNormalizer | 11 | 100% |
|
|
| PlusgiroNormalizer | 10 | 100% |
|
|
| AmountNormalizer | 15 | 100% |
|
|
| DateNormalizer | 19 | 93% |
|
|
| OrganisationNumberNormalizer | 11 | 100% |
|
|
| SupplierAccountsNormalizer | 13 | 100% |
|
|
| CustomerNumberNormalizer | 12 | 100% |
|
|
|
|
## 测试覆盖的场景
|
|
|
|
### 通用测试 (所有 normalizer)
|
|
|
|
- ✅ 空字符串处理
|
|
- ✅ None 值处理
|
|
- ✅ Callable 接口 (`__call__`)
|
|
- ✅ 基本功能验证
|
|
|
|
### InvoiceNumberNormalizer
|
|
|
|
- ✅ 纯数字发票号
|
|
- ✅ 带前缀的发票号 (INV-, etc.)
|
|
- ✅ 字母数字混合
|
|
- ✅ 特殊字符处理
|
|
- ✅ Unicode 字符清理
|
|
- ✅ 多个分隔符
|
|
- ✅ 无数字内容
|
|
- ✅ 重复变体去除
|
|
|
|
### OCRNormalizer
|
|
|
|
- ✅ 纯数字 OCR
|
|
- ✅ 带前缀 (OCR:)
|
|
- ✅ 空格分隔
|
|
- ✅ 连字符分隔
|
|
- ✅ 混合分隔符
|
|
- ✅ 超长 OCR 号码
|
|
|
|
### BankgiroNormalizer
|
|
|
|
- ✅ 8 位数字 (带/不带连字符)
|
|
- ✅ 7 位数字格式
|
|
- ✅ 特殊连字符类型 (en-dash, etc.)
|
|
- ✅ 空格处理
|
|
- ✅ 前缀处理 (BG:)
|
|
- ✅ OCR 错误变体生成
|
|
|
|
### PlusgiroNormalizer
|
|
|
|
- ✅ 8 位数字 (带/不带连字符)
|
|
- ✅ 7 位数字
|
|
- ✅ 9 位数字
|
|
- ✅ 空格处理
|
|
- ✅ 前缀处理 (PG:)
|
|
- ✅ OCR 错误变体生成
|
|
|
|
### AmountNormalizer
|
|
|
|
- ✅ 整数金额
|
|
- ✅ 逗号小数分隔符
|
|
- ✅ 点小数分隔符
|
|
- ✅ 空格千位分隔符
|
|
- ✅ 空格作为小数分隔符 (瑞典格式)
|
|
- ✅ 美国格式 (1,390.00)
|
|
- ✅ 欧洲格式 (1.390,00)
|
|
- ✅ 货币符号移除 (kr, SEK)
|
|
- ✅ 大金额处理
|
|
- ✅ 冒号破折号后缀 (1234:-)
|
|
|
|
### DateNormalizer
|
|
|
|
- ✅ ISO 格式 (2025-12-13)
|
|
- ✅ 欧洲斜杠格式 (13/12/2025)
|
|
- ✅ 欧洲点格式 (13.12.2025)
|
|
- ✅ 紧凑格式 YYYYMMDD
|
|
- ✅ 紧凑格式 YYMMDD
|
|
- ✅ 短年份格式 (DD.MM.YY)
|
|
- ✅ 瑞典月份名称 (december, dec)
|
|
- ✅ 瑞典月份缩写
|
|
- ✅ 带时间的 ISO 格式
|
|
- ✅ 歧义日期双重解析
|
|
- ✅ 中点分隔符
|
|
- ✅ 空格格式
|
|
- ✅ 无效日期处理
|
|
- ✅ 2 位年份世纪判断
|
|
|
|
### OrganisationNumberNormalizer
|
|
|
|
- ✅ 带/不带连字符
|
|
- ✅ VAT 号码提取
|
|
- ✅ VAT 号码生成
|
|
- ✅ 12 位带世纪组织号
|
|
- ✅ VAT 带空格
|
|
- ✅ 大小写混合 VAT 前缀
|
|
- ✅ OCR 错误变体生成
|
|
|
|
### SupplierAccountsNormalizer
|
|
|
|
- ✅ 单个 Plusgiro
|
|
- ✅ 单个 Bankgiro
|
|
- ✅ 多账号 (| 分隔)
|
|
- ✅ 前缀标准化
|
|
- ✅ 前缀带空格
|
|
- ✅ 空账号忽略
|
|
- ✅ 无前缀账号
|
|
- ✅ 7 位账号
|
|
- ✅ 10 位账号
|
|
- ✅ 混合格式账号
|
|
|
|
### CustomerNumberNormalizer
|
|
|
|
- ✅ 字母数字+空格+连字符
|
|
- ✅ 字母数字+空格
|
|
- ✅ 大小写变体
|
|
- ✅ 纯数字
|
|
- ✅ 仅连字符
|
|
- ✅ 仅空格
|
|
- ✅ 大写重复去除
|
|
- ✅ 复杂客户编号
|
|
- ✅ 瑞典客户编号格式 (UMJ 436-R)
|
|
|
|
## 最佳实践
|
|
|
|
### 1. 使用 pytest fixtures
|
|
|
|
每个测试类都使用 `@pytest.fixture` 创建 normalizer 实例:
|
|
|
|
```python
|
|
@pytest.fixture
|
|
def normalizer(self):
|
|
"""Create normalizer instance for testing"""
|
|
return InvoiceNumberNormalizer()
|
|
|
|
def test_something(self, normalizer):
|
|
result = normalizer.normalize('test')
|
|
assert 'expected' in result
|
|
```
|
|
|
|
### 2. 清晰的测试命名
|
|
|
|
测试方法名清楚描述测试场景:
|
|
|
|
```python
|
|
def test_with_dash_8_digits(self, normalizer):
|
|
"""8-digit Bankgiro with dash should generate variants"""
|
|
...
|
|
```
|
|
|
|
### 3. 断言具体行为
|
|
|
|
明确测试期望的行为:
|
|
|
|
```python
|
|
result = normalizer.normalize('5393-9484')
|
|
assert '5393-9484' in result # 保留原始格式
|
|
assert '53939484' in result # 生成无连字符格式
|
|
```
|
|
|
|
### 4. 边界条件测试
|
|
|
|
每个 normalizer 都测试:
|
|
- 空字符串
|
|
- None 值
|
|
- 特殊字符
|
|
- 极端值
|
|
|
|
### 5. 接口一致性测试
|
|
|
|
验证 callable 接口:
|
|
|
|
```python
|
|
def test_callable_interface(self, normalizer):
|
|
"""Normalizer should be callable via __call__"""
|
|
result = normalizer('test-value')
|
|
assert result is not None
|
|
```
|
|
|
|
## 添加新测试
|
|
|
|
为新功能添加测试:
|
|
|
|
```python
|
|
def test_new_feature(self, normalizer):
|
|
"""Description of what this tests"""
|
|
# Arrange
|
|
input_value = 'test-input'
|
|
|
|
# Act
|
|
result = normalizer.normalize(input_value)
|
|
|
|
# Assert
|
|
assert 'expected-output' in result
|
|
assert len(result) > 0
|
|
```
|
|
|
|
## CI/CD 集成
|
|
|
|
这些测试可以轻松集成到 CI/CD 流程:
|
|
|
|
```yaml
|
|
# .github/workflows/test.yml
|
|
- name: Run Normalizer Tests
|
|
run: pytest tests/normalize/normalizers/ -v --cov
|
|
```
|
|
|
|
## 总结
|
|
|
|
✅ **112 个测试**全部通过
|
|
✅ **高覆盖率**: 大部分 normalizer 达到 100%
|
|
✅ **快速执行**: 5.6 秒完成所有测试
|
|
✅ **清晰结构**: 每个 normalizer 独立测试文件
|
|
✅ **易维护**: 遵循 pytest 最佳实践
|