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]
CI/CD / test (push) Successful in 15s
CI/CD / nas-deploy (push) Successful in 47s
CI/CD / nas-verify (push) Successful in 23s

This commit is contained in:
2026-08-15 00:02:12 +08:00
parent f9322a7c48
commit b9315ef7e7
2 changed files with 45 additions and 40 deletions
+39 -35
View File
@@ -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: