fix(tests): Mac本地9个既有测试失败根治——test_cta_engine的sys.modules mock注入从import期挪进module-scoped fixture(pytest全量collection先于执行,import期注入污染后收集的metrics/datareader/factor)+venv310按lock补装empyrical-reloaded==0.5.12;影子柜台设计文档头更新(P1已上线/P3前半已实现) [nas]
This commit is contained in:
@@ -1,14 +1,23 @@
|
||||
"""Tests for sanguo_backtest.cta_engine module."""
|
||||
# Mock vnpy and tzlocal modules before importing anything that depends on them
|
||||
# Mock vnpy and tzlocal modules before importing anything that depends on them。
|
||||
# ⚠️注入必须在 fixture 内(测试期)而非模块 import 期:pytest 全量 collection 先于任何
|
||||
# 测试执行,import 期注入的 sys.modules mock 会污染后收集的 datareader/metrics/factor
|
||||
# 模块(它们 import 到的是 MagicMock)——2026-08-15 修,见 tests 全量 9 failed 根因。
|
||||
import sys
|
||||
import importlib
|
||||
import importlib.util
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
import json
|
||||
from unittest.mock import Mock, patch, MagicMock
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
import pandas as pd
|
||||
|
||||
# Only mock these when the real module is NOT importable (e.g. local env missing empyrical).
|
||||
# In the container (authoritative test env) everything imports fine, so NO mock is installed
|
||||
# → zero sys.modules pollution leaking into other test modules at collection time.
|
||||
# (Previous unconditional sys.modules[...]=MagicMock() here broke datareader/spike/factor/metrics
|
||||
# tests because it ran at collection time and never restored.)
|
||||
# → zero sys.modules pollution leaking into other test modules.
|
||||
mock_tzlocal = MagicMock()
|
||||
mock_tzlocal.get_localzone_name = MagicMock(return_value="UTC")
|
||||
_MOCK_FACTORIES = {
|
||||
@@ -18,41 +27,36 @@ _MOCK_FACTORIES = {
|
||||
"vnpy.trader.object": MagicMock,
|
||||
"vnpy.trader.database": MagicMock,
|
||||
"vnpy_ctastrategy.backtesting": MagicMock,
|
||||
"empyrical": MagicMock,
|
||||
}
|
||||
_SAVED_MODULES = {}
|
||||
for _name, _factory in _MOCK_FACTORIES.items():
|
||||
try:
|
||||
_found = importlib.util.find_spec(_name)
|
||||
except ModuleNotFoundError:
|
||||
_found = None
|
||||
if _found is None:
|
||||
_SAVED_MODULES[_name] = sys.modules.get(_name)
|
||||
sys.modules[_name] = _factory()
|
||||
|
||||
import pytest
|
||||
import json
|
||||
from unittest.mock import Mock, patch, MagicMock
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
import pandas as pd
|
||||
from sanguo_backtest.cta_engine import run_cta_backtest
|
||||
|
||||
|
||||
@pytest.fixture(scope="module", autouse=True)
|
||||
def _restore_modules_after():
|
||||
"""Restore sys.modules after this module's tests, so downstream tests import the
|
||||
REAL vnpy/empyrical instead of the mocks we installed above. Also drop cta_engine/
|
||||
metrics from the module cache (they were imported while mocks were active) so they
|
||||
re-import fresh with real dependencies."""
|
||||
yield
|
||||
for cached in ("sanguo_backtest.cta_engine", "sanguo_backtest.metrics", "sanguo_backtest.ashare_engine"):
|
||||
sys.modules.pop(cached, None)
|
||||
for k, orig in _SAVED_MODULES.items():
|
||||
if orig is None:
|
||||
sys.modules.pop(k, None)
|
||||
else:
|
||||
sys.modules[k] = orig
|
||||
def _mock_deps_and_import_cta_engine():
|
||||
"""module 级唯一 mock 窗口:装 mock(仅当真模块不可导入)→ import cta_engine →
|
||||
测试 → teardown 恢复 sys.modules 并清 sanguo_backtest 模块缓存(它们是在 mock
|
||||
生效期间 import 的,须弹出让后续测试用真依赖重新 import)。"""
|
||||
saved = {}
|
||||
for name, factory in _MOCK_FACTORIES.items():
|
||||
try:
|
||||
found = importlib.util.find_spec(name)
|
||||
except ModuleNotFoundError:
|
||||
found = None
|
||||
if found is None:
|
||||
saved[name] = sys.modules.get(name)
|
||||
sys.modules[name] = factory()
|
||||
try:
|
||||
cta_engine = importlib.import_module("sanguo_backtest.cta_engine")
|
||||
globals()["run_cta_backtest"] = cta_engine.run_cta_backtest
|
||||
yield
|
||||
finally:
|
||||
for cached in ("sanguo_backtest.cta_engine", "sanguo_backtest.metrics", "sanguo_backtest.ashare_engine"):
|
||||
sys.modules.pop(cached, None)
|
||||
for k, orig in saved.items():
|
||||
if orig is None:
|
||||
sys.modules.pop(k, None)
|
||||
else:
|
||||
sys.modules[k] = orig
|
||||
globals().pop("run_cta_backtest", None)
|
||||
|
||||
|
||||
class TestRunCtaBacktest:
|
||||
|
||||
Reference in New Issue
Block a user