Files
MCM-ICM_Toolkit/tests/test_api_surface.py
T

67 lines
2.2 KiB
Python
Raw Normal View History

"""公开 API 契约测试。
这组测试**不测算法本身**,只守两件事:
1. `__all__` 里承诺的名字,必须真的存在且可调用;
2. 子包的导出必须与顶层一致。
它现在就能通过,并且在后续实现过程中会立刻抓到"改了导出忘了改 `__all__`"这类问题。
"""
from __future__ import annotations
import importlib
import pytest
import mcm_toolkit as mcm
SUBPACKAGES = ["core", "data", "stats", "models", "solve", "decision", "viz"]
def test_version_is_string():
assert isinstance(mcm.__version__, str)
assert mcm.__version__.count(".") == 2
@pytest.mark.parametrize("name", SUBPACKAGES)
def test_subpackage_importable(name):
mod = importlib.import_module(f"mcm_toolkit.{name}")
assert hasattr(mod, "__all__"), f"mcm_toolkit.{name} 缺 __all__"
assert hasattr(mcm, name), f"顶层未导出子包 {name}"
@pytest.mark.parametrize("name", SUBPACKAGES)
def test_subpackage_all_resolves(name):
mod = importlib.import_module(f"mcm_toolkit.{name}")
missing = [n for n in mod.__all__ if not hasattr(mod, n)]
assert not missing, f"mcm_toolkit.{name}.__all__ 里有不存在的名字:{missing}"
def test_toplevel_all_resolves():
missing = [n for n in mcm.__all__ if not hasattr(mcm, n)]
assert not missing, f"顶层 __all__ 里有不存在的名字:{missing}"
def test_toplevel_shortcuts_are_callable():
for name in ["monte_carlo", "bootstrap_ci", "parameter_sweep", "topsis", "compare_models"]:
assert callable(getattr(mcm, name)), f"{name} 应当是可调用的快捷函数"
def test_no_star_import_pollution():
"""顶层不应把子包内部的函数全部摊平——只允许 __all__ 里那批。"""
allowed = set(mcm.__all__) | {"annotations"} # annotations 来自 __future__ 导入
leaked = [n for n in dir(mcm) if not n.startswith("_") and n not in allowed]
assert not leaked, f"顶层出现了 __all__ 之外的公开名字:{leaked}"
def test_result_dataclass_defaults():
r = mcm.Result("demo")
assert r.success is True
assert r.params is None
assert r.metrics is None
assert r.message == ""
def test_random_helpers_exist():
assert callable(mcm.set_seed)
assert callable(mcm.get_rng)