🔍 Code Extractor

class TestBaseValidator

Maturity: 52

Unit test class for testing the BaseValidator class functionality, including validation of extraction results, field types, date consistency, amount consistency, and entity-specific validation rules.

File:
/tf/active/vicechatdev/invoice_extraction/tests/test_validators.py
Lines:
75 - 204
Complexity:
moderate

Purpose

This test class provides comprehensive test coverage for the BaseValidator abstract class by creating a concrete implementation (ConcreteValidator) and testing all validation scenarios including required fields, field types, date consistency, amount calculations, and nested field access. It ensures the BaseValidator correctly identifies valid and invalid extraction results from document processing.

Source Code

class TestBaseValidator(unittest.TestCase):
    """Test cases for the BaseValidator class."""

    class ConcreteValidator(BaseValidator):
        """Concrete implementation of BaseValidator for testing."""
        def _entity_specific_validation(self, extraction_result, result):
            # Simple implementation for testing
            if 'test_field' in extraction_result:
                if extraction_result['test_field'] == 'invalid':
                    result.add_issue('test_field', 'Test field is invalid', 'error')
    
    def setUp(self):
        """Set up test environment before each test."""
        self.config = {
            'required_fields': {
                'invoice.number': 'critical',
                'vendor.name': 'critical',
                'amounts.total': 'important'
            }
        }
        self.validator = self.ConcreteValidator(self.config)
        
        # Sample valid extraction result
        self.valid_extraction = {
            'invoice': {
                'number': 'INV-12345',
                'issue_date': '2023-01-15',
                'due_date': '2023-02-15'
            },
            'vendor': {
                'name': 'Test Vendor',
                'address': '123 Test St',
                'vat_number': '123456789'
            },
            'amounts': {
                'subtotal': 500.00,
                'tax': 100.00,
                'total': 600.00,
                'currency': 'USD'
            }
        }
    
    def test_init(self):
        """Test initialization of BaseValidator."""
        self.assertEqual(self.validator.required_fields['invoice.number'], 'critical')
        self.assertIsInstance(self.validator.field_types, dict)
    
    def test_validate_valid_extraction(self):
        """Test validation of a valid extraction result."""
        result = self.validator.validate(self.valid_extraction)
        self.assertTrue(result.is_valid)
        self.assertEqual(len(result.issues), 0)
    
    def test_validate_missing_required_field(self):
        """Test validation with a missing required field."""
        # Remove a critical required field
        invalid_extraction = self.valid_extraction.copy()
        invalid_extraction['invoice'] = {}  # No invoice number
        
        result = self.validator.validate(invalid_extraction)
        self.assertFalse(result.is_valid)
        self.assertIn('invoice.number', result.field_issues)
    
    def test_validate_field_types(self):
        """Test validation of field types."""
        # Add invalid field type (string instead of number)
        invalid_extraction = self.valid_extraction.copy()
        invalid_extraction['amounts'] = {
            'subtotal': 'five hundred',  # Should be a number
            'tax': 100.00,
            'total': 600.00
        }
        
        result = self.validator.validate(invalid_extraction)
        # Field type issues are warnings by default
        self.assertTrue(result.is_valid)  # Still valid despite warnings
        self.assertEqual(len(result.warnings), 1)
    
    def test_validate_date_consistency(self):
        """Test validation of date consistency."""
        # Make due date before issue date
        invalid_extraction = self.valid_extraction.copy()
        invalid_extraction['invoice'] = {
            'number': 'INV-12345',
            'issue_date': '2023-02-15',  # Later date
            'due_date': '2023-01-15'     # Earlier date
        }
        
        result = self.validator.validate(invalid_extraction)
        self.assertFalse(result.is_valid)
        self.assertIn('invoice.due_date', result.field_issues)
    
    def test_validate_amount_consistency(self):
        """Test validation of amount consistency."""
        # Make subtotal + tax != total
        invalid_extraction = self.valid_extraction.copy()
        invalid_extraction['amounts'] = {
            'subtotal': 500.00,
            'tax': 100.00,
            'total': 700.00  # Incorrect total (should be 600)
        }
        
        result = self.validator.validate(invalid_extraction)
        # Amount inconsistency issues are warnings by default
        self.assertTrue(result.is_valid)  # Still valid despite warnings
        self.assertEqual(len(result.warnings), 1)
    
    def test_entity_specific_validation(self):
        """Test entity-specific validation."""
        # Add a test field that will trigger entity-specific validation
        extraction = self.valid_extraction.copy()
        extraction['test_field'] = 'invalid'
        
        result = self.validator.validate(extraction)
        self.assertFalse(result.is_valid)
        self.assertIn('test_field', result.field_issues)
    
    def test_get_nested_field(self):
        """Test getting nested fields by path."""
        # Test existing field
        value = self.validator._get_nested_field(self.valid_extraction, 'invoice.number')
        self.assertEqual(value, 'INV-12345')
        
        # Test non-existent field
        value = self.validator._get_nested_field(self.valid_extraction, 'invoice.non_existent')
        self.assertIsNone(value)
        
        # Test non-existent parent
        value = self.validator._get_nested_field(self.valid_extraction, 'non_existent.field')
        self.assertIsNone(value)

Parameters

Name Type Default Kind
bases unittest.TestCase -

Parameter Details

bases: Inherits from unittest.TestCase to provide testing framework functionality including assertions, test setup/teardown, and test discovery capabilities

Return Value

As a test class, it doesn't return values directly. Each test method returns None but uses assertions to verify expected behavior. Test methods validate that the BaseValidator produces correct ValidationResult objects with appropriate is_valid flags, issues, warnings, and field_issues dictionaries.

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_extraction

test_init(self) -> None

Purpose: Test that BaseValidator initializes correctly with required_fields and field_types

Returns: None - uses assertions to verify initialization

test_validate_valid_extraction(self) -> None

Purpose: Test that a valid extraction result passes validation without issues

Returns: None - asserts result.is_valid is True and no issues exist

test_validate_missing_required_field(self) -> None

Purpose: Test that validation fails when a critical required field is missing

Returns: None - asserts result.is_valid is False and field_issues contains the missing field

test_validate_field_types(self) -> None

Purpose: Test that field type validation detects incorrect data types (e.g., string instead of number)

Returns: None - asserts warnings are generated for type mismatches

test_validate_date_consistency(self) -> None

Purpose: Test that date logic validation detects inconsistencies like due_date before issue_date

Returns: None - asserts result.is_valid is False when dates are inconsistent

test_validate_amount_consistency(self) -> None

Purpose: Test that amount calculations are validated (subtotal + tax = total)

Returns: None - asserts warnings are generated for amount inconsistencies

test_entity_specific_validation(self) -> None

Purpose: Test that entity-specific validation rules defined in ConcreteValidator are executed

Returns: None - asserts custom validation logic is triggered and issues are recorded

test_get_nested_field(self) -> None

Purpose: Test the _get_nested_field method for accessing nested dictionary values using dot notation

Returns: None - asserts correct values are returned for existing fields and None for missing fields

Attributes

Name Type Description Scope
config dict Configuration dictionary containing required_fields mapping with field paths and criticality levels (critical, important) instance
validator ConcreteValidator Instance of ConcreteValidator (BaseValidator subclass) used for testing validation functionality instance
valid_extraction dict Sample valid extraction result containing nested dictionaries for invoice, vendor, and amounts data used as baseline for tests instance
ConcreteValidator class Inner class that extends BaseValidator to provide concrete implementation of _entity_specific_validation for testing purposes class

Dependencies

  • unittest
  • datetime
  • logging
  • validators.base_validator
  • validators.uk_validator
  • validators.be_validator
  • validators.au_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
from validators.be_validator import BEValidator
from validators.au_validator import AUValidator

Usage Example

import unittest
from validators.base_validator import BaseValidator, ValidationResult

class TestBaseValidator(unittest.TestCase):
    class ConcreteValidator(BaseValidator):
        def _entity_specific_validation(self, extraction_result, result):
            if 'test_field' in extraction_result:
                if extraction_result['test_field'] == 'invalid':
                    result.add_issue('test_field', 'Test field is invalid', 'error')
    
    def setUp(self):
        self.config = {
            'required_fields': {
                'invoice.number': 'critical',
                'vendor.name': 'critical',
                'amounts.total': 'important'
            }
        }
        self.validator = self.ConcreteValidator(self.config)
        self.valid_extraction = {
            'invoice': {'number': 'INV-12345', 'issue_date': '2023-01-15'},
            'vendor': {'name': 'Test Vendor'},
            'amounts': {'subtotal': 500.00, 'tax': 100.00, 'total': 600.00}
        }
    
    def test_validate_valid_extraction(self):
        result = self.validator.validate(self.valid_extraction)
        self.assertTrue(result.is_valid)
        self.assertEqual(len(result.issues), 0)

if __name__ == '__main__':
    unittest.main()

Best Practices

  • Always call setUp() before each test to ensure clean test state with fresh validator and extraction data
  • Use copy() when modifying valid_extraction to avoid test interdependencies
  • Test both valid and invalid scenarios to ensure comprehensive coverage
  • Verify both is_valid flag and specific issue collections (issues, warnings, field_issues)
  • Create a ConcreteValidator subclass to test abstract BaseValidator since it cannot be instantiated directly
  • Test nested field access patterns using dot notation (e.g., 'invoice.number')
  • Distinguish between critical errors (is_valid=False) and warnings (is_valid=True with warnings)
  • Test edge cases like missing fields, incorrect types, and logical inconsistencies
  • Use descriptive test method names that clearly indicate what is being tested
  • Group related assertions together to make test failures easier to diagnose

Similar Components

AI-powered semantic similarity - components with related functionality:

  • class TestBaseExtractor 77.7% similar

    Unit test class for testing the BaseExtractor class, which provides comprehensive test coverage for document extraction functionality including initialization, structure extraction, bounding box text retrieval, and confidence calculation.

    From: /tf/active/vicechatdev/invoice_extraction/tests/test_extractors.py
  • class BaseValidator 77.3% similar

    Abstract base class for validating extracted invoice data with entity-specific validation rules. Provides common validation functionality for required fields, field types, date consistency, and amount calculations.

    From: /tf/active/vicechatdev/invoice_extraction/validators/base_validator.py
  • class TestBEValidator 74.7% similar

    Unit test class for validating the BEValidator class, which validates Belgian invoice extraction results including VAT numbers, addresses, IBAN, currency, and legal requirements.

    From: /tf/active/vicechatdev/invoice_extraction/tests/test_validators.py
  • class TestUKValidator 70.1% similar

    Unit test class for validating the UKValidator class functionality, testing UK-specific invoice validation rules including VAT numbers, addresses, banking details, and currency.

    From: /tf/active/vicechatdev/invoice_extraction/tests/test_validators.py
  • class TestAUValidator 67.5% similar

    Unit test class for validating the AUValidator class, which validates Australian invoice extraction results including ABN, GST, banking details, and tax invoice requirements.

    From: /tf/active/vicechatdev/invoice_extraction/tests/test_validators.py
← Back to Browse