# 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 最佳实践