Implement Constructor Validation Tests
Purpose
Automatically generate comprehensive constructor validation tests for service classes that follow the fail-fast principle. This skill ensures every required parameter has a corresponding test that validates the service raises ValueError when that parameter is None.
When to Use
Use this skill when:
- •Creating tests for new services - Generating constructor validation tests automatically
- •Validating fail-fast principle - Ensuring services validate required parameters
- •Testing constructor validation - Verifying services raise ValueError for None parameters
- •Testing required parameters - Validating each required parameter has proper validation
- •Following TDD workflow - Creating tests before or alongside service implementation
- •Automating test generation - Reducing manual boilerplate for repetitive test patterns
Trigger phrases:
- •"Generate constructor validation tests"
- •"Test service constructor"
- •"Validate fail-fast principle"
- •"Test required parameters"
- •"Create constructor tests"
Quick Start
Simplest usage - generate tests for a new service:
# 1. Identify the service class you need tests for # 2. Locate or create the test file # 3. Invoke this skill with the service file path and test file path
The skill will:
- •Extract constructor signature from service class
- •Identify all required parameters
- •Generate one test method per required parameter
- •Include proper fixtures, type ignores, and error message assertions
Table of Contents
Core Sections
- •Purpose - Automated constructor validation test generation
- •Instructions - Step-by-step test generation process
- •Step 1: Analyze Service Constructor - Extract parameters and validation logic
- •Step 2: Identify Required Test Fixtures - Mock fixture patterns
- •Step 3: Generate Test Class Structure - Test class template
- •Step 4: Handle Type Ignore Comments - Suppress pyright errors
- •Step 5: Validate Error Messages - Assert specific error messages
- •Step 6: Organize Test Methods - Method ordering and fixture exclusion
- •Examples - Working implementations
- •Example 1: Simple Service (2 Parameters) - ChunkingService pattern
- •Example 2: Complex Service (5 Parameters) - IndexingOrchestrator pattern
Supporting Resources
- •templates/test-template.py - Copy-paste ready template
- •references/reference.md - Pattern analysis and edge cases
Utility Scripts
- •Find Missing Validation Tests - Identify services lacking constructor validation tests
- •Generate Constructor Tests - Auto-generate pytest test classes from service signatures
- •Validate Test Coverage - Verify all constructor parameters have corresponding tests
Advanced Topics
- •Requirements - Python 3.11+, pytest, fail-fast validation pattern
- •Automation Strategy - Future script for full automation
- •See Also - Related documentation
Instructions
Step 1: Analyze Service Constructor
Read the service class to extract:
- •Class name (for test naming)
- •Constructor parameters and their types
- •Validation error messages (from
if not X: raise ValueError("...")blocks) - •Dependencies that need mocking
Example Pattern to Look For:
def __init__(
self,
settings: Settings,
indexing_module: IndexingModule,
extractor_registry: ExtractorRegistry,
):
if not settings:
raise ValueError("Settings is required for IndexingOrchestrator")
if not indexing_module:
raise ValueError("IndexingModule is required for IndexingOrchestrator")
# ... more validations
Step 2: Identify Required Test Fixtures
For each constructor parameter:
- •Determine if it needs a mock fixture (services, repositories)
- •Identify the spec type for mocking
- •Check if the service has async methods (use AsyncMock)
Fixture Naming Pattern: mock_{parameter_name}
Step 3: Generate Test Class Structure
Create a dedicated test class for constructor validation:
class Test{ServiceName}Constructor:
"""Test {ServiceName} constructor and initialization."""
# Fixtures for each dependency
@pytest.fixture
def mock_settings(self):
"""Create mock Settings."""
return MagicMock(spec=Settings)
# ... more fixtures
# Success case test
def test_constructor_initializes_with_valid_dependencies(self, ...):
"""Test that constructor properly initializes with all valid dependencies."""
# Arrange + Act
instance = ServiceName(...)
# Assert
assert instance.settings is mock_settings
# ... assert all dependencies
# Validation tests (one per parameter)
def test_constructor_fails_when_{param}_is_none(self, ...):
"""Test that constructor raises ValueError when {Param} is None."""
with pytest.raises(ValueError) as exc_info:
ServiceName(
param=None, # type: ignore
# ... other valid params
)
assert "{Param} is required for {ServiceName}" in str(exc_info.value)
Step 4: Handle Type Ignore Comments
For each None parameter in validation tests:
- •Add
# type: ignorecomment on same line - •This suppresses pyright errors for intentionally wrong types
- •Required because we're testing failure cases
Pattern:
ServiceName(
settings=None, # type: ignore
other_param=mock_other_param,
)
Step 5: Validate Error Messages
Each validation test must assert the specific error message:
assert "{ParamType} is required for {ServiceName}" in str(exc_info.value)
Error Message Pattern from Constructor:
if not settings:
raise ValueError("Settings is required for IndexingOrchestrator")
Step 6: Organize Test Methods
Method Ordering:
- •Success case (all valid parameters)
- •Validation failures (one per parameter, in parameter order)
Fixture Exclusion Pattern: Each validation test excludes the fixture for the parameter being tested:
def test_constructor_fails_when_settings_is_none(
self,
# mock_settings EXCLUDED - this is what we're testing
mock_indexing_module,
mock_extractor_registry,
):
Examples
Example 1: Simple Service (2 Parameters)
Service Constructor:
class ChunkingService:
def __init__(self, settings: Settings, tokenizer: Tokenizer):
if not settings:
raise ValueError("Settings is required for ChunkingService")
if not tokenizer:
raise ValueError("Tokenizer is required for ChunkingService")
self.settings = settings
self.tokenizer = tokenizer
Generated Tests:
class TestChunkingServiceConstructor:
"""Test ChunkingService constructor and initialization."""
@pytest.fixture
def mock_settings(self):
"""Create mock Settings."""
return MagicMock(spec=Settings)
@pytest.fixture
def mock_tokenizer(self):
"""Create mock Tokenizer."""
return MagicMock(spec=Tokenizer)
def test_constructor_initializes_with_valid_dependencies(
self, mock_settings, mock_tokenizer
):
"""Test that constructor properly initializes with all valid dependencies."""
service = ChunkingService(
settings=mock_settings,
tokenizer=mock_tokenizer,
)
assert service.settings is mock_settings
assert service.tokenizer is mock_tokenizer
def test_constructor_fails_when_settings_is_none(self, mock_tokenizer):
"""Test that constructor raises ValueError when Settings is None."""
with pytest.raises(ValueError) as exc_info:
ChunkingService(
settings=None, # type: ignore
tokenizer=mock_tokenizer,
)
assert "Settings is required for ChunkingService" in str(exc_info.value)
def test_constructor_fails_when_tokenizer_is_none(self, mock_settings):
"""Test that constructor raises ValueError when Tokenizer is None."""
with pytest.raises(ValueError) as exc_info:
ChunkingService(
settings=mock_settings,
tokenizer=None, # type: ignore
)
assert "Tokenizer is required for ChunkingService" in str(exc_info.value)
Example 2: Complex Service (5 Parameters)
For a complex service with 5 parameters, follow the same pattern as Example 1 above. Create a test class with fixtures for each parameter, a success case test, and one validation failure test for each required parameter. See references/reference.md for detailed pattern analysis and edge cases.
Requirements
- •Python 3.11+
- •pytest installed:
uv pip install pytest - •unittest.mock (standard library)
- •Service class must follow fail-fast validation pattern
- •Service must have
if not param: raise ValueError(...)blocks
Automation Strategy
This skill is a prime candidate for full automation because:
- •Highly Repetitive Pattern - 100+ test classes use identical structure
- •Deterministic Generation - Output is fully determined by input (constructor signature)
- •Clear Rules - No subjective decisions required
- •Type Safety - Can validate against service class signature
- •Error Message Consistency - Standard format:
"{Type} is required for {Service}"
Future Enhancement: Create a script that:
- •Accepts service file path as input
- •Parses AST to extract constructor signature
- •Generates complete test class with all fixtures and validation tests
- •Runs pyright to validate generated tests
- •Writes to appropriate test file location
See Also
- •templates/test-template.py - Copy-paste template
- •references/reference.md - Pattern analysis and edge cases
- •CLAUDE.md - Fail-fast principle documentation