ワンクリックで
optional-dependency-test-mocking
Pattern for testing modules with optional dependencies by mocking at sys.modules level
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Pattern for testing modules with optional dependencies by mocking at sys.modules level
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Normalize long-form CODEX cycle folders to short form before notebooks run. Trigger: cyc001_reg001_*, hard-coded cyc paths breaking, staged CODEX raw data failing in Notebooks 1/2.
v5.6.0 joint multi-TF model: single model per symbol with broadcast 1Hour context replaces dual 15Min/1Hour models. Trigger: (1) replacing weighted-voting model aggregation, (2) adding broadcast features to vectorized env, (3) limited training data + worried about overfitting from doubling obs_dim, (4) backtest builder mismatch with newer feature counts.
DEPRECATED in v5.6.0 — see joint-multi-tf-v560 skill. Documents the v5.2.0 dual-model approach (train separate 15Min/1Hour models, combine via weighted voting). Still relevant for: (1) loading legacy v5.5.0 dual models, (2) understanding the historical aggregation layer, (3) resampling pattern via origin='start'.
Surface a shipped-but-undocumented CLI feature in user-facing docs. Trigger: user reports a known feature missing from README/readthedocs even though the CLI command exists.
KINTSUGI Snakefile + CLI changes that route SLURM jobs around accounts saturated by OTHER users on the same QOS pool. Trigger: QOSGrpMemLimit, jobs stuck pending despite available GPU slots in config, noisy neighbor on shared QOS, multi-user investment pool exhaustion, _build_cycle_assignment static-vs-live.
KINTSUGI SLURM batch processing: Maximize throughput using multi-account resource calculation with GPU+CPU pools per account. Trigger: SLURM job submission, batch processing, resource maximization, GPU+CPU concurrent, headless processing, resource pool.
| name | optional-dependency-test-mocking |
| description | Pattern for testing modules with optional dependencies by mocking at sys.modules level |
| author | Claude Code |
| date | "2026-02-22T00:00:00.000Z" |
| version | 1.0.0 |
| Item | Details |
|---|---|
| Date | 2026-02-22 |
| Goal | Test modules that depend on optional packages without installing them |
| Environment | Python 3.11+, pytest |
| Status | Success — validated with 137 tests for CCXT broker |
When a module uses try/except ImportError for optional dependencies, you can't just unittest.mock.patch the import — the module-level guard has already run. Instead, inject a mock module into sys.modules before importing the module under test.
import sys
import types
from unittest.mock import MagicMock
# Create mock module BEFORE importing the module under test
_mock_pkg = types.ModuleType('optional_package')
# CRITICAL: Use real exception classes, not MagicMock
# MagicMock exceptions break `except SomeError` clauses
class _BaseError(Exception):
pass
class _SpecificError(_BaseError):
pass
_mock_pkg.BaseError = _BaseError
_mock_pkg.SpecificError = _SpecificError
# Mock callable classes (exchange constructors, clients, etc.)
for name in ['ClassA', 'ClassB']:
setattr(_mock_pkg, name, MagicMock(name=f'pkg.{name}'))
# Inject into sys.modules
sys.modules['optional_package'] = _mock_pkg
# NOW import — the try/except will find our mock
from my_project.module import MyClass, MY_FLAG # noqa: E402
@pytest.fixture(autouse=True)
def _patch_optional_package():
"""Ensure mock stays in sys.modules even if other tests remove it."""
prev = sys.modules.get('optional_package')
sys.modules['optional_package'] = _mock_pkg
yield
if prev is not None:
sys.modules['optional_package'] = prev
else:
sys.modules.pop('optional_package', None)
@pytest.fixture(autouse=True)
def _reset_mock_state():
"""Reset all mocks between tests to prevent cross-test leakage."""
yield
for name in ['ClassA', 'ClassB']:
getattr(_mock_pkg, name).reset_mock()
# In production code:
try:
exchange.fetch_balance()
except ccxt.InsufficientFunds: # This is an `except` clause
handle_insufficient()
# If InsufficientFunds is a MagicMock:
# - `raise MagicMock()` works
# - But `except MagicMock` FAILS — except needs a real BaseException subclass
# - Tests pass where they shouldn't, hiding real bugs
Always create exception classes as real Python classes inheriting from Exception (or the appropriate base), preserving the inheritance hierarchy from the real package.
When multiple test files need the same mock, guard against double-injection:
if 'optional_package' not in sys.modules:
_mock_pkg = types.ModuleType('optional_package')
# ... set up mock ...
sys.modules['optional_package'] = _mock_pkg
This prevents conflicts when pytest collects multiple test files that share the dependency.
| Attempt | Why it Failed | Lesson Learned |
|---|---|---|
@patch('module.ccxt') | Module-level try/except already ran at import time | Must inject BEFORE import |
MagicMock() for exceptions | except MagicMock is a TypeError | Use real Exception subclasses |
| Shared conftest.py mock | Import order depends on pytest collection order | Each file should be self-contained |
| No autouse fixture | Other test files could remove the mock | Always restore in autouse fixture |
| No reset fixture | Mock call counts leaked between tests | Reset mocks after each test |
monkeypatch.setitem in fixture | Only works for existing keys, not pre-import injection | Use direct sys.modules assignment at module level |
See the Alpaca Trading CCXT test suite:
tests/test_ccxt_broker.py — 102 tests, full mock ccxt with 4 exception classestests/test_exchange_selector.py — 35 tests, guard-style injection for second filetry/except ImportError runs at import time, not call timeexcept MockClass is a TypeError in Pythonsys.modules injectionAVAILABLE flags work automatically — if the module checks PACKAGE_AVAILABLE = True inside the try block, your mock makes it True