CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-dirty-equals

Python library that leverages the __eq__ method to make unit tests more declarative and readable through flexible comparison classes.

Pending
Overview
Eval results
Files

dict-types.mddocs/

Dictionary Types

Dictionary comparison with flexible matching strategies including partial matching, strict ordering, and key filtering. These types enable sophisticated validation of dictionary structures with configurable constraints for different use cases.

Capabilities

IsDict

Configurable dictionary comparison that serves as the base for all dictionary validation. Supports multiple matching modes through the settings method and direct instantiation with expected key-value pairs.

class IsDict(DirtyEquals):
    """
    Flexible dictionary comparison with configurable matching behavior.
    
    Supports exact matching, partial matching, key filtering, and order enforcement
    through various configuration options.
    """
    
    def __init__(self, *expected_args, **expected_kwargs):
        """
        Initialize dictionary comparison (overloaded constructor).
        
        Can be called in multiple ways:
        - IsDict() - matches any dictionary
        - IsDict(expected_dict) - matches specific dictionary  
        - IsDict(key1=value1, key2=value2) - matches with keyword arguments
        - IsDict(expected_dict, key3=value3) - combines dict and kwargs
        
        Args:
            *expected_args: Expected dictionary as positional argument
            **expected_kwargs: Expected key-value pairs as keyword arguments
        """
    
    def settings(
        self,
        *,
        strict: Optional[bool] = None,
        partial: Optional[bool] = None, 
        ignore: Optional[Sequence[str]] = None
    ) -> 'IsDict':
        """
        Configure dictionary matching behavior.
        
        Args:
            strict: If True, enforce key order and exact matching
            partial: If True, allow subset matching (only check specified keys)
            ignore: List of keys to ignore during comparison
            
        Returns:
            IsDict: New instance with updated settings
        """
    
    @property
    def expected_values(self) -> Dict[str, Any]:
        """Get expected key-value pairs."""
    
    @property  
    def strict(self) -> bool:
        """Whether strict mode (order enforcement) is enabled."""
    
    @property
    def partial(self) -> bool:
        """Whether partial mode (subset matching) is enabled."""
    
    @property
    def ignore(self) -> Optional[Sequence[str]]:
        """Keys to ignore during comparison."""
    
    def equals(self, other: Any) -> bool:
        """
        Check if value matches dictionary constraints.
        
        Args:
            other: Value to validate (should be dict-like)
            
        Returns:
            bool: True if value satisfies all conditions
        """

Usage Examples

from dirty_equals import IsDict, IsPositive, IsStr

# Basic dictionary matching
test_dict = {'a': 1, 'b': 2}
assert test_dict == IsDict({'a': 1, 'b': 2})
assert test_dict == IsDict(a=1, b=2)

# Match any dictionary
assert test_dict == IsDict()
assert {'x': 'y'} == IsDict()

# Combine positional and keyword arguments
base_dict = {'a': 1}
assert {'a': 1, 'b': 2} == IsDict(base_dict, b=2)

# With value validators
user_data = {
    'id': 123,
    'name': 'John',
    'email': 'john@example.com',
    'active': True
}

assert user_data == IsDict({
    'id': IsPositive,
    'name': IsStr(min_length=1),
    'email': IsStr(regex=r'.+@.+\..+'),
    'active': True
})

# Using settings for flexible matching
config = {'debug': True, 'port': 8080, 'host': 'localhost'}

# Partial matching - only check specified keys
assert config == IsDict({'debug': True}).settings(partial=True)
assert config == IsDict(debug=True).settings(partial=True)

# Ignore specific keys
assert config == IsDict({
    'debug': True, 
    'port': 8080
}).settings(ignore=['host'])

# Strict mode - enforce order (for ordered dicts)
from collections import OrderedDict
ordered = OrderedDict([('a', 1), ('b', 2)])
assert ordered == IsDict(OrderedDict([('a', 1), ('b', 2)])).settings(strict=True)

# Chaining configurations
partial_ignored = IsDict(debug=True).settings(partial=True, ignore=['timestamp'])
test_config = {'debug': True, 'env': 'test', 'timestamp': '2023-01-01'}
assert test_config == partial_ignored

IsPartialDict

Specialized dictionary comparison that only validates specified keys, ignoring any additional keys in the actual dictionary. Equivalent to IsDict(...).settings(partial=True).

class IsPartialDict(IsDict):
    """
    Partial dictionary matching - validates only specified keys.
    
    Checks that the target dictionary contains at least the specified keys
    with matching values, but allows additional keys to be present.
    """
    
    def __init__(self, *expected_args, **expected_kwargs):
        """
        Initialize partial dictionary comparison.
        
        Args:
            *expected_args: Expected dictionary as positional argument
            **expected_kwargs: Expected key-value pairs as keyword arguments
        """

Usage Examples

from dirty_equals import IsPartialDict, IsPositive

# Basic partial matching
full_dict = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
assert full_dict == IsPartialDict({'a': 1, 'b': 2})
assert full_dict == IsPartialDict(a=1, b=2)

# API response validation - ignore extra fields
api_response = {
    'user_id': 123,
    'username': 'john_doe',
    'email': 'john@example.com',
    'created_at': '2023-01-01T00:00:00Z',
    'last_login': '2023-01-15T12:30:00Z',
    'profile_picture': 'https://example.com/pic.jpg',
    'preferences': {'theme': 'dark', 'language': 'en'}
}

# Only validate essential fields
assert api_response == IsPartialDict({
    'user_id': IsPositive,
    'username': 'john_doe',
    'email': IsStr
})

# Database record validation - focus on updated fields  
updated_record = {
    'id': 456,
    'name': 'Updated Name',
    'email': 'updated@example.com', 
    'updated_at': '2023-01-15T12:00:00Z',
    'version': 2,
    # ... many other fields we don't care about for this test
}

assert updated_record == IsPartialDict({
    'name': 'Updated Name',
    'email': 'updated@example.com'
})

# Nested partial matching
complex_data = {
    'user': {
        'id': 123,
        'profile': {'name': 'John', 'age': 30, 'city': 'NYC'},
        'settings': {'notifications': True}
    },
    'metadata': {'version': 1, 'source': 'api'}
}

assert complex_data == IsPartialDict({
    'user': IsPartialDict({
        'id': 123,
        'profile': IsPartialDict({'name': 'John'})
    })
})

IsIgnoreDict

Dictionary comparison that ignores keys with None values during matching. Useful when dealing with APIs or databases that include null fields that should be excluded from validation.

class IsIgnoreDict(IsDict):
    """
    Dictionary comparison ignoring None values.
    
    Performs dictionary matching but excludes any key-value pairs
    where the value is None from the comparison.
    """
    
    def __init__(self, *expected_args, **expected_kwargs):
        """
        Initialize dictionary comparison that ignores None values.
        
        Args:
            *expected_args: Expected dictionary as positional argument
            **expected_kwargs: Expected key-value pairs as keyword arguments
        """

Usage Examples

from dirty_equals import IsIgnoreDict, IsStr

# Basic None value ignoring
test_dict = {'a': 1, 'b': None, 'c': 'hello'}
expected = {'a': 1, 'c': 'hello'}
assert test_dict == IsIgnoreDict(expected)

# API response with null fields
api_data = {
    'user_id': 123,
    'username': 'john_doe',
    'first_name': 'John',
    'last_name': None,  # Not provided
    'middle_name': None,  # Not provided
    'email': 'john@example.com',
    'phone': None  # Not provided
}

# Validate ignoring null fields
assert api_data == IsIgnoreDict({
    'user_id': 123,
    'username': 'john_doe', 
    'first_name': 'John',
    'email': IsStr
})

# Database record with optional fields
db_record = {
    'id': 456,
    'title': 'Test Article',
    'content': 'Article content...',
    'author_id': 123,
    'editor_id': None,  # No editor assigned
    'published_at': '2023-01-01T00:00:00Z',
    'featured_image': None,  # No image
    'tags': ['tech', 'python']
}

assert db_record == IsIgnoreDict({
    'id': 456,
    'title': 'Test Article',
    'content': IsStr,
    'author_id': 123,
    'published_at': IsStr,
    'tags': ['tech', 'python']
})

# Form data processing
form_input = {
    'name': 'John Doe',
    'email': 'john@example.com',
    'company': 'Acme Corp',
    'phone': None,  # Optional field not filled
    'address': None,  # Optional field not filled
    'newsletter': True
}

# Validate required fields only
assert form_input == IsIgnoreDict({
    'name': IsStr,
    'email': IsStr,
    'company': IsStr,
    'newsletter': True
})

IsStrictDict

Dictionary comparison with strict ordering enforcement. Validates both the key-value pairs and their order, useful for ordered dictionaries or when insertion order matters.

class IsStrictDict(IsDict):
    """
    Strict dictionary comparison with order enforcement.
    
    Validates dictionary contents and enforces that keys appear
    in the same order as specified in the expected dictionary.
    """
    
    def __init__(self, *expected_args, **expected_kwargs):
        """
        Initialize strict dictionary comparison.
        
        Args:
            *expected_args: Expected dictionary as positional argument
            **expected_kwargs: Expected key-value pairs as keyword arguments
        """

Usage Examples

from dirty_equals import IsStrictDict
from collections import OrderedDict

# Order-sensitive dictionary matching
ordered_dict = OrderedDict([('first', 1), ('second', 2), ('third', 3)])
assert ordered_dict == IsStrictDict(OrderedDict([('first', 1), ('second', 2), ('third', 3)]))

# Regular dict with insertion order (Python 3.7+)
test_dict = {'a': 1, 'b': 2, 'c': 3}
assert test_dict == IsStrictDict({'a': 1, 'b': 2, 'c': 3})

# This would fail due to different order:
# different_order = {'b': 2, 'a': 1, 'c': 3}  
# assert different_order == IsStrictDict({'a': 1, 'b': 2, 'c': 3})  # False

# Configuration validation where order matters
config_sections = {
    'database': {'host': 'localhost', 'port': 5432},
    'cache': {'host': 'redis', 'port': 6379},
    'logging': {'level': 'INFO', 'file': 'app.log'}
}

# Ensure sections appear in specific order
expected_order = {
    'database': IsStrictDict({'host': 'localhost', 'port': 5432}),
    'cache': IsStrictDict({'host': 'redis', 'port': 6379}),
    'logging': IsStrictDict({'level': 'INFO', 'file': 'app.log'})
}
assert config_sections == IsStrictDict(expected_order)

# API response with ordered fields
api_response = OrderedDict([
    ('status', 'success'),
    ('data', {'id': 123, 'name': 'John'}),
    ('metadata', {'timestamp': '2023-01-01T00:00:00Z'})
])

assert api_response == IsStrictDict({
    'status': 'success',
    'data': {'id': 123, 'name': 'John'},
    'metadata': {'timestamp': IsStr}
})

# JSON object with preserved field order
import json
json_string = '{"id": 123, "name": "John", "email": "john@example.com"}'
parsed = json.loads(json_string, object_pairs_hook=OrderedDict)

assert parsed == IsStrictDict(OrderedDict([
    ('id', 123),
    ('name', 'John'),
    ('email', IsStr)
]))

Type Definitions

from typing import Any, Dict, Optional, Sequence, Union

# All dictionary types inherit from IsDict which inherits from DirtyEquals
# They work with standard Python dict objects and dict-like objects

Install with Tessl CLI

npx tessl i tessl/pypi-dirty-equals

docs

base-types.md

boolean-types.md

datetime-types.md

dict-types.md

index.md

inspection-types.md

numeric-types.md

other-types.md

sequence-types.md

string-types.md

tile.json