import pytest
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
# 1. 模拟生成真实业务数据
def get_sample_data():
np.random.seed(42)
df = pd.DataFrame({
'age': np.random.randint(18, 65, 100),
'score': np.random.uniform(50, 100, 100),
'target': np.random.choice([0, 1], 100)
})
return df
# 2. 编写 5 大契约测试函数
def test_schema_contract():
"""契约 1: Schema 与关键列名必须存在"""
df = get_sample_data()
required_columns = {'age', 'score', 'target'}
assert required_columns.issubset(df.columns), "❌ 数据集中缺少必须的特征列!"
def test_no_missing_contract():
"""契约 2: 预处理后核心列不得含有 NaN"""
df = get_sample_data()
assert df[['age', 'score']].isnull().sum().sum() == 0, "❌ 特征列中存在未处理的缺失值 NaN!"
def test_split_conservation_contract():
"""契约 3: 切分样本数量守恒契约"""
df = get_sample_data()
X = df[['age', 'score']]
y = df['target']
X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.2, random_state=42)
assert len(X_tr) + len(X_te) == len(X), "❌ 切分后训练集与测试集行数和不等于原始总行数!"
def test_shape_alignment_contract():
"""契约 4: 特征矩阵行数与标签 y 长度必须绝对匹配"""
df = get_sample_data()
X = df[['age', 'score']]
y = df['target']
assert X.shape[0] == len(y), "❌ 特征矩阵 X 的行数与标签 y 的长度不一致!"
def test_prediction_probability_contract():
"""契约 5: 模型输出概率值必须落入 [0.0, 1.0] 区间"""
df = get_sample_data()
X = df[['age', 'score']]
y = df['target']
model = LogisticRegression().fit(X, y)
probs = model.predict_proba(X)[:, 1]
assert np.all((probs >= 0.0) & (probs <= 1.0)), "❌ 模型输出概率超出了 [0, 1] 合法闭区间!"
# 运行测试集 (演示触发)
if __name__ == "__main__":
print("================ 开始执行 pytest 5 大契约测试 ================")
test_schema_contract()
test_no_missing_contract()
test_split_conservation_contract()
test_shape_alignment_contract()
test_prediction_probability_contract()
print("✅ 所有 5 大工程与数据质量契约测试 100% 顺利通过!")