Skip to content

Generated tests use fragile try/except ImportError pattern for mocking #413

@jiaminc-cmu

Description

@jiaminc-cmu

Summary

PDD generates tests that use try/except ImportError to conditionally install mocks in sys.modules. This pattern is fragile and breaks when modules exist but require external services (Secret Manager, databases, APIs).

Problem

PDD generates code like this:

# 2. Mock src.config
try:
    import src.config        # If this succeeds, mock is SKIPPED
except ImportError:          
    mock_config = MagicMock()
    sys.modules["src.config"] = mock_config

# 3. Mock src.clients.secrets_client
try:
    import src.clients.secrets_client  # If this succeeds, mock is SKIPPED
except ImportError:                     
    mock_secrets_mod = MagicMock()
    sys.modules["src.clients.secrets_client"] = mock_secrets_mod

The assumption is wrong: This pattern assumes that if a module imports successfully, it doesn't need mocking. But modules can import fine while still requiring external services at runtime.

Real-World Failure

In a generated github_app_2 project:

  1. src.clients.secrets_client imports successfully (it's valid Python code)
  2. The try/except ImportError block is skipped - no mock installed
  3. Test calls get_installation_token() which calls secrets_client.get_github_private_key()
  4. The real secrets_client tries to connect to Google Secret Manager
  5. Test hangs indefinitely waiting for GCP credentials that don't exist
# This test hangs forever:
tests/test_github_client.py::test_get_installation_token_success

Why It Worked During Sync

During pdd sync, the environment was different:

  • Possibly different import order
  • Possibly missing dependencies that caused ImportError
  • The _run.json shows "tests_passed": 16, "tests_failed": 0

But running the same tests now (with all dependencies installed) causes hangs because the mocks are never applied.

The Anti-Pattern

# BAD: Only mocks if import fails
try:
    import some_module
except ImportError:
    sys.modules["some_module"] = MagicMock()

# GOOD: Always mock external dependencies in tests
@pytest.fixture
def mock_secrets_client():
    with patch("src.clients.github_client.secrets_client") as mock:
        mock.get_github_private_key.return_value = "fake-key"
        yield mock

Impact

  1. Tests hang: Real services are called instead of mocks
  2. False confidence: Sync reports success but tests don't actually run correctly
  3. Environment-dependent: Tests pass in some environments, hang/fail in others
  4. Hard to debug: Hangs give no error message, just timeout

Suggested Fix

PDD should:

  1. Never use try/except ImportError for mocking - this pattern is fundamentally flawed for testing
  2. Always mock external dependencies unconditionally using unittest.mock.patch or pytest fixtures
  3. Mock at the point of use, not at import time:
    # Mock where the module is used, not where it's defined
    with patch("src.clients.github_client.secrets_client") as mock:
        ...
  4. Identify external dependencies (secrets, databases, APIs, network) and always mock them regardless of import success

Related

This is related to #412 (incorrect sys.modules paths) but is a separate issue about the conditional mocking pattern itself.

Environment

  • PDD version: latest (as of 2026-01-27)
  • Python: 3.12
  • All dependencies installed (github, google-cloud-secret-manager, etc.)

Metadata

Metadata

Labels

bugSomething isn't workingpdd

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions