Skip to content

refactor(tests): extract shared isolated_module_loader for backend-module unit tests #803

Open

Description

@AndriiPasternak31

Summary

Four unit test files load real backend modules in isolation by stubbing heavy deps in sys.modules at import time, then spec_from_file_location + exec_module against the real source file. The plumbing is ~50 lines of near-identical boilerplate per file:

  • tests/unit/test_telegram_webhook_backfill.py
  • tests/unit/test_inject_assigned_credentials.py
  • tests/unit/test_subscription_auto_switch_no_cred_import.py
  • tests/unit/test_cleanup_unreachable_orphan.py

Each one independently rediscovers (or copies from a neighbor):

  1. Which slots to stub in sys.modules to keep the target import side-effect-free.
  2. How to spec-load the module under test from its file path.
  3. How to snapshot/restore sys.modules so the stubs don't leak across the pytest session (the _STUBBED_MODULE_NAMES + _restore_sys_modules escape hatch from tests/lint_sys_modules.py).

This is the technique that needs to exist for any test of backend code that depends on heavy modules the unit venv doesn't carry (docker, the full database.py stack, …) — but every new such test pays the discoverability + boilerplate tax.

Proposed design

Add tests/testing_utils/isolated_module_loader.py with one entry point:

```python
def load_isolated(
module_source_path: Path,
*,
stubs: dict[str, ModuleType | Mock],
snapshot_names: list[str],
module_name: str | None = None,
) -> ModuleType:
"""Spec-load a backend module by file path with sys.modules stubs.

Stubs are planted under the names in `stubs.keys()`, then the target
module is exec'd. The function returns the loaded module. Cleanup
happens via a pytest-plugin-registered autouse fixture: any test
file that imports this module gets sys.modules snapshot/restore
automatically for the union of all `stubs.keys()` and `snapshot_names`.
\"\"\"

```

A pytest plugin in tests/testing_utils/conftest.py (or registered via pytest_plugins) auto-applies the snapshot/restore fixture for any test file that imports load_isolated. This eliminates the need for each test to declare _STUBBED_MODULE_NAMES + _restore_sys_modules by hand.

Migration plan

  1. Land the helper + plugin with green tests against an empty consumer (smoke test only).
  2. Migrate each of the four files in a separate small PR — keep the diff per file to ~30 lines net (delete 50 lines of plumbing, add ~20 lines of helper call + assertions).
  3. After all four migrate cleanly, tighten tests/lint_sys_modules.py: ban bare _STUBBED_MODULE_NAMES declarations OUTSIDE the helper itself; the helper becomes the only path to the escape hatch.

Why this matters

Out of scope

This issue does NOT propose removing the named-helper escape hatch from the lint script. The hatch remains the documented exit; the helper just makes it the obvious one.

Refs

Metadata

Metadata

Assignees

No one assigned

    Labels

    complexity-lowComplexity: low (board points 1-3)priority-p3Nice-to-havestatus-readyGreenlit and ready for development (vetted; counterpart to status-incubating)theme-devexTheme: DevExtype-refactorCode improvement

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions