Claude
Skills
Sign in
Back

test-implement-constructor-validation

Included with Lifetime
$97 forever

Auto-generate constructor validation tests from service class signatures. Use when creating tests for new services, validating fail-fast principle, ensuring constructor validation, or testing required parameters. Analyzes service __init__ methods and generates pytest.raises(ValueError) tests for each required parameter. Works with Python service classes following dependency injection pattern. High automation opportunity (100+ test classes use this pattern).

Code Reviewscripts

What this skill does


# 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:

```bash
# 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/find_missing_validation.py) - Identify services lacking constructor validation tests
- [Generate Constructor Tests](./scripts/generate_constructor_tests.py) - Auto-generate pytest test classes from service signatures
- [Validate Test Coverage](./scripts/validate_constructor_tests.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:**
```python
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:

```python
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: ignore` comment on same line
- This suppresses pyright errors for intentionally wrong types
- Required because we're testing failure cases

**Pattern:**
```python
ServiceName(
    settings=None,  # type: ignore
    other_param=mock_other_param,
)
```

### Step 5: Validate Error Messages

Each validation test must assert the specific error message:

```python
assert "{ParamType} is required for {ServiceName}" in str(exc_info.value)
```

**Error Message Pattern from Constructor:**
```python
if not settings:
    raise ValueError("Settings is required for IndexingOrchestrator")
```

### Step 6: Organize Test Methods

**Method Ordering:**
1. Success case (all valid parameters)
2. Validation failures (one per parameter, in parameter order)

**Fixture Exclusion Pattern:**
Each validation test excludes the fixture for the parameter being tested:

```python
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:**
```python
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:**
```python
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,
        )

Related in Code Review