class TestBEValidator
Unit test class for validating the BEValidator class, which validates Belgian invoice extraction results including VAT numbers, addresses, IBAN, currency, and legal requirements.
/tf/active/vicechatdev/invoice_extraction/tests/test_validators.py
347 - 482
moderate
Purpose
This test class provides comprehensive test coverage for the BEValidator class, ensuring it correctly validates Belgian-specific invoice data including VAT number format (BE prefix), Belgian addresses, standard VAT rates (21%, 12%, 6%, 0%), IBAN format, EUR currency, and mandatory legal requirements like invoice dates. It tests both valid and invalid scenarios, verifying that critical errors fail validation while warnings allow validation to pass.
Source Code
class TestBEValidator(unittest.TestCase):
"""Test cases for the BEValidator class."""
def setUp(self):
"""Set up test environment before each test."""
self.config = {
'required_fields': {
'invoice.number': 'critical',
'vendor.name': 'critical',
'amounts.total': 'critical'
},
'be_vat_rates': [21, 12, 6, 0]
}
self.validator = BEValidator(self.config)
# Sample valid Belgian extraction result
self.valid_be_extraction = {
'invoice': {
'number': 'FACT-12345',
'issue_date': '2023-01-15',
'due_date': '2023-02-15'
},
'vendor': {
'name': 'Belgian Test SPRL',
'address': '123 Rue de Bruxelles, 1000 Bruxelles, Belgique',
'vat_number': 'BE0123.456.789'
},
'amounts': {
'subtotal': 500.00,
'vat': 105.00,
'total': 605.00,
'vat_rate': 21,
'currency': 'EUR'
},
'payment': {
'iban': 'BE68 5390 0754 7034',
'bic': 'KREDBEBB'
}
}
def test_validate_be_extraction(self):
"""Test validation of a valid Belgian extraction result."""
result = self.validator.validate(self.valid_be_extraction)
self.assertTrue(result.is_valid)
self.assertEqual(len(result.issues), 0)
def test_validate_be_vat_number(self):
"""Test validation of Belgian VAT number format."""
# Test invalid VAT number format
invalid_extraction = self.valid_be_extraction.copy()
invalid_extraction['vendor'] = {
'name': 'Belgian Test SPRL',
'address': '123 Rue de Bruxelles, 1000 Bruxelles, Belgique',
'vat_number': '0123456789' # Missing BE prefix
}
result = self.validator.validate(invalid_extraction)
self.assertFalse(result.is_valid) # Belgian VAT format is critical
self.assertGreaterEqual(len(result.issues), 1)
# Test completely wrong format
invalid_extraction['vendor']['vat_number'] = 'INVALID-VAT'
result = self.validator.validate(invalid_extraction)
self.assertFalse(result.is_valid)
self.assertGreaterEqual(len(result.issues), 1)
def test_validate_be_address(self):
"""Test validation of Belgian address."""
# Test non-Belgian address
invalid_extraction = self.valid_be_extraction.copy()
invalid_extraction['vendor'] = {
'name': 'Belgian Test SPRL',
'address': '123 Main St, Berlin, 10115, Germany', # Not Belgian address
'vat_number': 'BE0123.456.789'
}
result = self.validator.validate(invalid_extraction)
self.assertTrue(result.is_valid) # Address is a warning, not error
self.assertGreaterEqual(len(result.warnings), 1)
def test_validate_vat_rate(self):
"""Test validation of Belgian VAT rates."""
# Test invalid VAT rate
invalid_extraction = self.valid_be_extraction.copy()
invalid_extraction['amounts']['vat_rate'] = 18 # Not a standard Belgian rate
result = self.validator.validate(invalid_extraction)
self.assertTrue(result.is_valid) # VAT rate is a warning, not error
self.assertGreaterEqual(len(result.warnings), 1)
def test_validate_be_iban(self):
"""Test validation of Belgian IBAN."""
# Test invalid IBAN
invalid_extraction = self.valid_be_extraction.copy()
invalid_extraction['payment'] = {
'iban': 'FR76 3000 6000 0112 3456 7890 189', # French IBAN
'bic': 'KREDBEBB'
}
result = self.validator.validate(invalid_extraction)
self.assertTrue(result.is_valid) # IBAN issues are warnings, not errors
self.assertGreaterEqual(len(result.warnings), 1)
# Test malformatted IBAN
invalid_extraction['payment'] = {
'iban': 'BE12345', # Too short
'bic': 'KREDBEBB'
}
result = self.validator.validate(invalid_extraction)
self.assertTrue(result.is_valid) # IBAN issues are warnings, not errors
self.assertGreaterEqual(len(result.warnings), 1)
def test_validate_be_currency(self):
"""Test validation of Belgian currency."""
# Test non-Euro currency
invalid_extraction = self.valid_be_extraction.copy()
invalid_extraction['amounts']['currency'] = 'USD' # Not EUR
result = self.validator.validate(invalid_extraction)
self.assertTrue(result.is_valid) # Currency is a warning, not error
self.assertGreaterEqual(len(result.warnings), 1)
def test_validate_be_legal_requirements(self):
"""Test validation of Belgian legal invoice requirements."""
# Test missing invoice date (mandatory in Belgium)
invalid_extraction = self.valid_be_extraction.copy()
invalid_extraction['invoice'] = {
'number': 'FACT-12345',
# Missing issue_date
'due_date': '2023-02-15'
}
result = self.validator.validate(invalid_extraction)
self.assertFalse(result.is_valid)
self.assertGreaterEqual(len(result.issues), 1)
Parameters
| Name | Type | Default | Kind |
|---|---|---|---|
bases |
unittest.TestCase | - |
Parameter Details
bases: Inherits from unittest.TestCase to provide testing framework functionality including assertions, test discovery, and test execution capabilities
Return Value
As a test class, it does not return values directly. Each test method performs assertions that either pass or fail. The setUp method initializes test fixtures including a BEValidator instance and sample valid Belgian extraction data. Test methods validate different aspects and return None, with pass/fail determined by assertions.
Class Interface
Methods
setUp(self) -> None
Purpose: Initialize test fixtures before each test method runs, creating a BEValidator instance and sample valid Belgian extraction data
Returns: None - sets up instance attributes self.config, self.validator, and self.valid_be_extraction
test_validate_be_extraction(self) -> None
Purpose: Test that a completely valid Belgian extraction result passes validation with no issues
Returns: None - asserts that validation result is valid with zero issues
test_validate_be_vat_number(self) -> None
Purpose: Test validation of Belgian VAT number format, ensuring invalid formats (missing BE prefix, completely wrong format) fail validation
Returns: None - asserts that invalid VAT numbers cause validation to fail with at least one issue
test_validate_be_address(self) -> None
Purpose: Test validation of Belgian addresses, verifying that non-Belgian addresses generate warnings but don't fail validation
Returns: None - asserts that non-Belgian addresses produce warnings but validation remains valid
test_validate_vat_rate(self) -> None
Purpose: Test validation of Belgian VAT rates (21%, 12%, 6%, 0%), ensuring non-standard rates generate warnings
Returns: None - asserts that invalid VAT rates produce warnings but validation remains valid
test_validate_be_iban(self) -> None
Purpose: Test validation of Belgian IBAN format, checking that non-Belgian IBANs and malformed IBANs generate warnings
Returns: None - asserts that IBAN issues produce warnings but validation remains valid
test_validate_be_currency(self) -> None
Purpose: Test validation of Belgian currency (EUR), ensuring non-Euro currencies generate warnings
Returns: None - asserts that non-EUR currency produces warnings but validation remains valid
test_validate_be_legal_requirements(self) -> None
Purpose: Test validation of Belgian legal invoice requirements, ensuring mandatory fields like issue_date cause validation failure when missing
Returns: None - asserts that missing mandatory fields cause validation to fail with at least one issue
Attributes
| Name | Type | Description | Scope |
|---|---|---|---|
config |
dict | Configuration dictionary containing required_fields with criticality levels and be_vat_rates list, used to initialize BEValidator | instance |
validator |
BEValidator | Instance of BEValidator class being tested, initialized with self.config in setUp() | instance |
valid_be_extraction |
dict | Sample valid Belgian invoice extraction result containing invoice, vendor, amounts, and payment information, used as baseline for test variations | instance |
Dependencies
unittestdatetimeloggingvalidators.base_validatorvalidators.be_validator
Required Imports
import unittest
from datetime import datetime
import logging
from validators.base_validator import BaseValidator
from validators.base_validator import ValidationResult
from validators.be_validator import BEValidator
Usage Example
import unittest
from validators.be_validator import BEValidator
# Run a single test
test = TestBEValidator()
test.setUp()
test.test_validate_be_extraction()
# Run all tests in the class
if __name__ == '__main__':
suite = unittest.TestLoader().loadTestsFromTestCase(TestBEValidator)
runner = unittest.TextTestRunner(verbosity=2)
runner.run(suite)
# Or use unittest discovery
# python -m unittest test_module.TestBEValidator
Best Practices
- Each test method is independent and can run in isolation
- setUp() is called before each test method to ensure clean state
- Test data is created fresh in setUp() to avoid test interdependencies
- Tests use deep copies of valid_be_extraction to avoid mutation affecting other tests
- Tests verify both positive (valid data) and negative (invalid data) scenarios
- Critical validation failures should set is_valid to False, while warnings should keep is_valid True
- Each test focuses on a single aspect of validation (VAT number, address, IBAN, etc.)
- Assertions check both the validation result and the presence of issues/warnings
- Run tests using unittest discovery or test runner for proper execution
Tags
Similar Components
AI-powered semantic similarity - components with related functionality:
-
class BEValidator 86.5% similar
-
class TestBEExtractor 84.6% similar
-
class TestUKValidator 78.3% similar
-
class TestBaseValidator 74.7% similar
-
class TestAUValidator 73.0% similar