-
Notifications
You must be signed in to change notification settings - Fork 48
Description
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_modThe 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:
src.clients.secrets_clientimports successfully (it's valid Python code)- The
try/except ImportErrorblock is skipped - no mock installed - Test calls
get_installation_token()which callssecrets_client.get_github_private_key() - The real secrets_client tries to connect to Google Secret Manager
- 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.jsonshows"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 mockImpact
- Tests hang: Real services are called instead of mocks
- False confidence: Sync reports success but tests don't actually run correctly
- Environment-dependent: Tests pass in some environments, hang/fail in others
- Hard to debug: Hangs give no error message, just timeout
Suggested Fix
PDD should:
- Never use
try/except ImportErrorfor mocking - this pattern is fundamentally flawed for testing - Always mock external dependencies unconditionally using
unittest.mock.patchor pytest fixtures - 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: ...
- 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.)