Skip to content

Instantly share code, notes, and snippets.

@gcrsaldanha
Created November 4, 2025 16:30
Show Gist options
  • Select an option

  • Save gcrsaldanha/1529b573f2c5781d3331a0ec8eae1cd6 to your computer and use it in GitHub Desktop.

Select an option

Save gcrsaldanha/1529b573f2c5781d3331a0ec8eae1cd6 to your computer and use it in GitHub Desktop.
Python Mock Safety: Always Use Autospec

Mock Safety with Autospec in Python Tests

Always use autospec=True or create_autospec() when mocking services, classes, or objects.

Quick Reference

# ❌ BAD: Mock allows non-existent methods
mock_service = Mock()
mock_service.get_user_data.return_value = {...}  # Typo - no error!

# ✅ GOOD: Autospec catches non-existent methods
mock_service = create_autospec(UserService)
mock_service.get_user_data.return_value = {...}  # AttributeError - method doesn't exist!
mock_service.get_user_info.return_value = {...}  # Correct method name

Why This Matters

  • Catches typos in method names at test time
  • Enforces correct signatures (args, kwargs)
  • Tests fail when service API changes
  • Prevents testing against phantom methods

Enforcement Patterns

  • Use mock.create_autospec(ClassName) for class mocks
  • Use @patch('module.Class', autospec=True) for decorators
  • Use mocker.patch('module.Class', autospec=True) with pytest-mock

Full Example: The Danger of Mock Without Autospec

# Service being tested
class InvestmentService:
    def get_portfolio_summary(self, user_id: int) -> dict:
        """Get portfolio summary for user"""
        pass

# ❌ WITHOUT AUTOSPEC - Test passes but code is broken
def test_portfolio_view_without_autospec(mocker):
    # Mock without autospec - anything goes
    mock_service = mocker.patch('eshares.investments.services.InvestmentService')
    
    # TYPO in method name - test still passes!
    mock_service.return_value.get_portfilio_summary.return_value = {
        'total_value': 10000
    }
    
    response = client.get('/api/portfolio/123')
    
    # Test passes ✓ 
    # Production breaks ✗ - AttributeError: 'InvestmentService' has no attribute 'get_portfilio_summary'
    assert response.status_code == 200


# ✅ WITH AUTOSPEC - Test catches the error
def test_portfolio_view_with_autospec(mocker):
    # Mock with autospec - enforces real API
    mock_service = mocker.patch(
        'eshares.investments.services.InvestmentService',
        autospec=True
    )
    
    # TYPO in method name - test fails immediately!
    mock_service.return_value.get_portfilio_summary.return_value = {
        'total_value': 10000
    }
    # AttributeError: Mock object has no attribute 'get_portfilio_summary'
    
    # Fix the typo:
    mock_service.return_value.get_portfolio_summary.return_value = {
        'total_value': 10000
    }
    
    response = client.get('/api/portfolio/123')
    
    # Test passes ✓
    # Production works ✓
    assert response.status_code == 200

Real-World Scenario This Prevents

  1. Service method renamed during refactoring: calculate_tax()calculate_tax_liability()
  2. Without autospec: All tests keep passing with old method name
  3. Production deploys, everything breaks 💥
  4. With autospec: Tests fail immediately, forcing you to update mocks ✅

Adding to Your Project Standards

Add this to your CLAUDE.md or testing guidelines:

### Mock Safety with Autospec

Always use `autospec=True` or `create_autospec()` when mocking services, classes, or objects to ensure mocks match real APIs and catch refactoring errors early.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment