class TestUKValidator
Unit test class for validating the UKValidator class functionality, testing UK-specific invoice validation rules including VAT numbers, addresses, banking details, and currency.
/tf/active/vicechatdev/invoice_extraction/tests/test_validators.py
207 - 344
moderate
Purpose
This test class provides comprehensive test coverage for the UKValidator class, which validates UK-specific invoice extraction results. It tests validation of UK VAT numbers (GB prefix format), UK addresses, standard UK VAT rates (20%, 5%, 0%), UK banking details (sort codes and account numbers), GBP currency, and VAT calculation accuracy. The tests verify that UK-specific validation rules produce appropriate warnings for non-compliant data while maintaining overall validation success for non-critical issues.
Source Code
class TestUKValidator(unittest.TestCase):
"""Test cases for the UKValidator class."""
def setUp(self):
"""Set up test environment before each test."""
self.config = {
'required_fields': {
'invoice.number': 'critical',
'vendor.name': 'critical',
'amounts.total': 'critical'
},
'uk_vat_rates': [20, 5, 0]
}
self.validator = UKValidator(self.config)
# Sample valid UK extraction result
self.valid_uk_extraction = {
'invoice': {
'number': 'INV-12345',
'issue_date': '2023-01-15',
'due_date': '2023-02-15'
},
'vendor': {
'name': 'UK Test Ltd',
'address': '123 London Road, London, W1A 1AA, UK',
'vat_number': 'GB123456789'
},
'amounts': {
'subtotal': 500.00,
'vat': 100.00,
'total': 600.00,
'vat_rate': 20,
'currency': 'GBP'
},
'payment': {
'sort_code': '12-34-56',
'account_number': '12345678'
}
}
def test_validate_uk_extraction(self):
"""Test validation of a valid UK extraction result."""
result = self.validator.validate(self.valid_uk_extraction)
self.assertTrue(result.is_valid)
self.assertEqual(len(result.issues), 0)
def test_validate_uk_vat_number(self):
"""Test validation of UK VAT number format."""
# Test invalid VAT number format
invalid_extraction = self.valid_uk_extraction.copy()
invalid_extraction['vendor'] = {
'name': 'UK Test Ltd',
'address': '123 London Road, London, W1A 1AA, UK',
'vat_number': '123456789' # Missing GB prefix
}
result = self.validator.validate(invalid_extraction)
self.assertTrue(result.is_valid) # VAT format is a warning, not error
self.assertGreaterEqual(len(result.warnings), 1)
# Test completely wrong format
invalid_extraction['vendor']['vat_number'] = 'INVALID-VAT'
result = self.validator.validate(invalid_extraction)
self.assertTrue(result.is_valid) # VAT format is a warning, not error
self.assertGreaterEqual(len(result.warnings), 1)
def test_validate_uk_address(self):
"""Test validation of UK address."""
# Test non-UK address
invalid_extraction = self.valid_uk_extraction.copy()
invalid_extraction['vendor'] = {
'name': 'UK Test Ltd',
'address': '123 Main St, New York, NY 10001, USA', # Not UK address
'vat_number': 'GB123456789'
}
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 UK VAT rates."""
# Test invalid VAT rate
invalid_extraction = self.valid_uk_extraction.copy()
invalid_extraction['amounts']['vat_rate'] = 17 # Not a standard UK 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_uk_banking_details(self):
"""Test validation of UK banking details."""
# Test invalid sort code
invalid_extraction = self.valid_uk_extraction.copy()
invalid_extraction['payment'] = {
'sort_code': '1234-56', # Wrong format
'account_number': '12345678'
}
result = self.validator.validate(invalid_extraction)
self.assertTrue(result.is_valid) # Banking details are warnings, not errors
self.assertGreaterEqual(len(result.warnings), 1)
# Test invalid account number
invalid_extraction['payment'] = {
'sort_code': '12-34-56',
'account_number': '123456' # Too short
}
result = self.validator.validate(invalid_extraction)
self.assertTrue(result.is_valid) # Banking details are warnings, not errors
self.assertGreaterEqual(len(result.warnings), 1)
def test_validate_uk_currency(self):
"""Test validation of UK currency."""
# Test non-UK currency
invalid_extraction = self.valid_uk_extraction.copy()
invalid_extraction['amounts']['currency'] = 'EUR' # Not GBP
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_vat_calculation(self):
"""Test validation of VAT calculation."""
# Test inconsistent VAT calculation
invalid_extraction = self.valid_uk_extraction.copy()
invalid_extraction['amounts'] = {
'subtotal': 500.00,
'vat': 120.00, # Should be 100.00 (20% of 500)
'total': 620.00,
'vat_rate': 20,
'currency': 'GBP'
}
result = self.validator.validate(invalid_extraction)
self.assertTrue(result.is_valid) # Calculation issues are warnings, not errors
self.assertGreaterEqual(len(result.warnings), 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. Test methods validate that the UKValidator.validate() method returns ValidationResult objects with appropriate is_valid flags and warnings/issues lists.
Class Interface
Methods
setUp(self) -> None
Purpose: Initialize test environment before each test method runs, creating validator instance and sample data
Returns: None - sets up instance attributes self.config, self.validator, and self.valid_uk_extraction
test_validate_uk_extraction(self) -> None
Purpose: Test that a completely valid UK extraction result passes validation without issues
Returns: None - performs assertions on validation result
test_validate_uk_vat_number(self) -> None
Purpose: Test validation of UK VAT number format, verifying that invalid formats (missing GB prefix, completely wrong format) generate warnings
Returns: None - performs assertions on validation warnings
test_validate_uk_address(self) -> None
Purpose: Test validation of UK address format, verifying that non-UK addresses generate warnings
Returns: None - performs assertions on validation warnings
test_validate_vat_rate(self) -> None
Purpose: Test validation of UK VAT rates, verifying that non-standard rates (not 20%, 5%, or 0%) generate warnings
Returns: None - performs assertions on validation warnings
test_validate_uk_banking_details(self) -> None
Purpose: Test validation of UK banking details including sort code format (XX-XX-XX) and account number length (8 digits)
Returns: None - performs assertions on validation warnings for invalid sort codes and account numbers
test_validate_uk_currency(self) -> None
Purpose: Test validation of UK currency, verifying that non-GBP currencies generate warnings
Returns: None - performs assertions on validation warnings
test_validate_vat_calculation(self) -> None
Purpose: Test validation of VAT calculation accuracy, verifying that incorrect VAT amounts generate warnings
Returns: None - performs assertions on validation warnings for calculation mismatches
Attributes
| Name | Type | Description | Scope |
|---|---|---|---|
config |
dict | Configuration dictionary containing required_fields with criticality levels and uk_vat_rates list | instance |
validator |
UKValidator | Instance of UKValidator class being tested, initialized with self.config | instance |
valid_uk_extraction |
dict | Sample valid UK invoice extraction result containing invoice, vendor, amounts, and payment sections with UK-specific data formats | instance |
Dependencies
unittestdatetimeloggingvalidators.base_validatorvalidators.uk_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.uk_validator import UKValidator
Usage Example
import unittest
from validators.uk_validator import UKValidator
from tests.test_validators import TestUKValidator
# Run all tests in the class
suite = unittest.TestLoader().loadTestsFromTestCase(TestUKValidator)
runner = unittest.TextTestRunner(verbosity=2)
result = runner.run(suite)
# Run a specific test
test = TestUKValidator('test_validate_uk_extraction')
test.setUp()
test.test_validate_uk_extraction()
# Run tests from command line
# python -m unittest tests.test_validators.TestUKValidator
# python -m unittest tests.test_validators.TestUKValidator.test_validate_uk_vat_number
Best Practices
- Each test method is independent and can run in isolation
- setUp() method is called before each test to ensure clean state
- Tests use deep copies of valid_uk_extraction to avoid state pollution between tests
- Tests verify both positive cases (valid data) and negative cases (invalid data)
- Tests check that UK-specific validation issues generate warnings rather than errors
- All test methods should start with 'test_' prefix for test discovery
- Use self.assertTrue(), self.assertEqual(), self.assertGreaterEqual() for assertions
- Test data includes realistic UK-specific formats (GB VAT numbers, UK postcodes, sort codes)
- Tests validate that non-critical issues (VAT format, address, currency) don't fail overall validation
Tags
Similar Components
AI-powered semantic similarity - components with related functionality:
-
class UKValidator 85.7% similar
-
class TestUKExtractor 83.2% similar
-
class TestAUValidator 78.5% similar
-
class TestBEValidator 78.3% similar
-
class TestBaseValidator 70.1% similar