SKILL.md
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](#purpose) - Automated constructor validation test generation
- [Instructions](#instructions) - Step-by-step test generation process
- [Step 1: Analyze Service Constructor](#step-1-analyze-service-constructor) - Extract parameters and validation logic - [Step 2: Identify Required Test Fixtures](#step-2-identify-required-test-fixtures) - Mock fixture patterns - [Step 3: Generate Test Class Structure](#step-3-generate-test-class-structure) - Test class template - [Step 4: Handle Type Ignore Comments](#step-4-handle-type-ignore-comments) - Suppress pyright errors - [Step 5: Validate Error Messages](#step-5-validate-error-messages) - Assert specific error messages - [Step 6: Organize Test Methods](#step-6-organize-test-methods) - Method ordering and fixture exclusion
- [Examples](#examples) - Working implementations
- [Example 1: Simple Service (2 Parameters)](#example-1-simple-service-2-parameters) - ChunkingService pattern - [Example 2: Complex Service (5 Parameters)](#example-2-complex-service-5-parameters) - IndexingOrchestrator pattern
Supporting Resources
- [templates/test-template.py](./templates/test-template.py) - Copy-paste ready template
- [references/reference.md](./references/reference.md) - Pattern analysis and edge cases
Utility Scripts
- [Find Missing Validation Tests](./scripts/findmissingvalidation.py) - Identify services lacking constructor validation tests
- [Generate Constructor Tests](./scripts/generateconstructortests.py) - Auto-generate pytest test classes from service signatures
- [Validate Test Coverage](./scripts/validateconstructortests.py) - Verify all constructor parameters have corresponding tests
Advanced Topics
- [Requirements](#requirements) - Python 3.11+, pytest, fail-fast validation pattern
- [Automation Strategy](#automation-strategy) - Future script for full automation
- [See Also](#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{parametername}
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](./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](./templates/test-template.py) - Copy-paste template
- [references/reference.md](./references/reference.md) - Pattern analysis and edge cases
- [CLAUDE.md](../../../../CLAUDE.md) - Fail-fast principle documentation