自动化测试框架与工具

专栏:人工智能训练师(三级)备考全攻略
模块:卷三·知识体系 — 第四部分·模型测试与评估
难度:⭐⭐⭐☆☆
考试权重:中频(选择+工具辨析)


一、为什么需要自动化测试?

手动测试的问题
┌─────────────────────────────────────────────────┐
│  模型每天更新 → 手动跑几百条用例 → 耗时费力       │
│  测试人员请假 → 无人测试 → 质量失控              │
│  回归测试 → 每次重复劳动 → 效率低下              │
│  人为疏忽 → 遗漏测试 → 上线出问题                │
└─────────────────────────────────────────────────┘

自动化测试的价值
  ✓ 每次提交自动运行,秒级反馈
  ✓ 覆盖面广,不易遗漏
  ✓ CI/CD集成,上线前自动卡控
  ✓ 测试报告自动生成,可追溯

二、自动化测试框架选型

2.1 主流框架对比

框架 适用模型类型 核心功能 学习成本 语言
pytest 所有(通用Python测试) 单元/集成/参数化测试 Python
Great Expectations 数据+ML Pipeline 数据质量验证、异常检测 Python
DeepChecks ML/DL模型 全自动模型评估、数据漂移检测 Python
Model Defender TensorFlow模型 对抗鲁棒性测试 Python
AI Verify AI系统 公平性+可解释性检测 Python
LangSmith LLM应用 Prompt测试、Chain追踪 Python
Promptfoo LLM/Prompt Prompt自动化评估 JS/Python

2.2 框架选择决策树

你的测试需求是什么?
│
├─ 数据质量验证
│   └→ Great Expectations ✅
│
├─ 传统ML/DL模型评估
│   └→ DeepChecks ✅
│
├─ LLM/Prompt测试
│   ├─ 需要Chain追踪 → LangSmith
│   └─ 需要多模型对比 → Promptfoo
│
├─ 安全性与公平性
│   └→ AI Verify ✅
│
└─ 通用自动化测试
    └→ pytest + 自定义断言 ✅

三、pytest 实战:模型测试自动化

3.1 项目结构

ml_project/
├── model/
│   ├── __init__.py
│   └── classifier.py        # 待测试的模型
├── tests/
│   ├── __init__.py
│   ├── conftest.py           # 共享fixtures
│   ├── test_unit.py          # 单元测试
│   ├── test_integration.py   # 集成测试
│   ├── test_robustness.py    # 鲁棒性测试
│   └── golden_set.json       # 标准测试集
├── pytest.ini
└── requirements.txt

3.2 完整测试代码

# =========================================
# conftest.py — 共享测试 fixtures
# =========================================
import pytest
import numpy as np
import json

@pytest.fixture
def trained_model():
    """加载训练好的模型"""
    from model.classifier import SentimentClassifier
    model = SentimentClassifier.load("model/latest/")
    return model

@pytest.fixture
def golden_set():
    """加载Golden Set标准测试数据"""
    with open("tests/golden_set.json", "r", encoding="utf-8") as f:
        return json.load(f)

@pytest.fixture
def sample_texts():
    """常用测试文本"""
    return {
        "positive": ["产品非常好用", "物流很快,满意", "性价比高,推荐"],
        "negative": ["质量差,退货", "太糟糕了", "客服态度恶劣"],
        "neutral":  ["今天天气不错", "快递已发出", "订单已收到"]
    }

# =========================================
# test_unit.py — 模型单元测试
# =========================================
import pytest

class TestModelUnit:
    """模型单元测试"""
    
    def test_output_shape(self, trained_model):
        """验证输出维度"""
        result = trained_model.predict(["测试文本"])
        assert len(result) == 1
        assert "label" in result[0]
        assert "confidence" in result[0]
    
    def test_confidence_range(self, trained_model):
        """验证置信度在[0,1]范围内"""
        result = trained_model.predict(["测试文本"])
        assert 0 <= result[0]["confidence"] <= 1
    
    def test_empty_input(self, trained_model):
        """验证空输入处理"""
        with pytest.raises(ValueError):
            trained_model.predict([""])
    
    def test_batch_prediction(self, trained_model):
        """验证批量预测"""
        texts = ["好评", "差评", "一般"]
        results = trained_model.predict(texts)
        assert len(results) == 3
    
    def test_long_text(self, trained_model):
        """验证长文本不崩溃"""
        long_text = "这个产品" * 500  # 1500字
        result = trained_model.predict([long_text])
        assert len(result) == 1

# =========================================
# test_integration.py — Golden Set集成测试
# =========================================
class TestGoldenSet:
    """Golden Set回归测试"""
    
    def test_golden_set_accuracy(self, trained_model, golden_set):
        """在Golden Set上验证准确率"""
        correct = 0
        total = len(golden_set)
        
        for item in golden_set:
            result = trained_model.predict([item["text"]])
            if result[0]["label"] == item["expected_label"]:
                correct += 1
        
        accuracy = correct / total
        assert accuracy >= 0.90, \
            f"Golden Set准确率{accuracy:.2%}低于阈值90%"
    
    def test_golden_set_no_regression(self, trained_model, golden_set):
        """与基线结果对比,不允许退化"""
        baseline_results = json.load(
            open("tests/baseline_results.json", "r")
        )
        
        current_labels = [
            trained_model.predict([item["text"]])[0]["label"]
            for item in golden_set
        ]
        
        mismatches = sum(
            1 for cur, base in zip(current_labels, baseline_results)
            if cur != base
        )
        
        assert mismatches == 0, \
            f"发现{mismatches}条回归用例与基线不一致"

# =========================================
# test_robustness.py — 鲁棒性测试
# =========================================
class TestRobustness:
    """鲁棒性测试"""
    
    @pytest.mark.parametrize("perturbation", [
        "add_punctuation",   # 添加多余标点
        "add_whitespace",    # 添加多余空格
        "typo",              # 同音错别字
        "uppercase",         # 全大写
    ])
    def test_perturbation_stability(
        self, trained_model, perturbation
    ):
        """验证模型对文本扰动的鲁棒性"""
        original = "这个产品非常好用"
        
        perturbed = {
            "add_punctuation": "这个产品非常好用!!!??",
            "add_whitespace": "这 个 产 品 非 常 好 用",
            "typo": "这个产口非常好用",  # 错别字
            "uppercase": "这个产品非常好用".upper(),
        }[perturbation]
        
        orig_result = trained_model.predict([original])[0]
        pert_result = trained_model.predict([perturbed])[0]
        
        assert orig_result["label"] == pert_result["label"], \
            f"扰动类型'{perturbation}'导致分类不一致"
    
    def test_special_characters(self, trained_model):
        """验证特殊字符不崩溃"""
        special_texts = [
            "<script>alert(1)</script>",   # XSS攻击
            "你好😊👋🎉",                   # Emoji
            "产品\t很\t好\n换行测试",        # 控制字符
            "PYTHON",                # 全角字符
        ]
        for text in special_texts:
            result = trained_model.predict([text])
            assert len(result) == 1  # 不崩溃即可

3.3 运行测试与报告

# 运行全部测试
pytest tests/ -v

# 运行指定类型测试
pytest tests/test_robustness.py -v

# 生成HTML报告(需安装 pytest-html)
pytest tests/ -v --html=report.html --self-contained-html

# 只运行上次失败的测试
pytest tests/ --lf

# 并行运行(需安装 pytest-xdist)
pytest tests/ -n auto

四、DeepChecks:一键模型体检

# =========================================
# DeepChecks — 自动化模型评估
# pip install deepchecks
# =========================================
from deepchecks.tabular.suites import model_evaluation_suite
from deepchecks.tabular import Dataset
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split

# 加载数据
data = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(
    data.data, data.target, test_size=0.2, random_state=42
)

# 训练模型
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)

# 创建DeepChecks数据集
train_ds = Dataset(X_train, label=y_train,
                   cat_features=[], features=data.feature_names)
test_ds = Dataset(X_test, label=y_test,
                  cat_features=[], features=data.feature_names)

# 运行模型评估套件(自动检测20+问题)
suite = model_evaluation_suite()
result = suite.run(train_ds, test_ds, model)

# 查看结果
result.show()  # 浏览器中打开可视化报告

# 输出示例:
# ✅ Simple Model Comparison         PASS
# ✅ Train Test Performance           PASS
# ❌ Weak Segments Performance        FAIL  (发现弱势子群体)
# ⚠️ Feature Drift                    WARN  (特征分布变化)
# ✅ New Labels                       PASS

五、CI/CD 集成自动化测试

5.1 完整CI/CD Pipeline

代码提交 → 自动化测试流水线

┌──────────────────────────────────────────────────────┐
│  Git Push                                              │
│    │                                                   │
│    ▼                                                   │
│  ① 数据验证测试(Great Expectations)                    │
│    │  通过?                                            │
│    ├─ 否 → 阻断,发送告警                               │
│    ▼ 是                                                │
│  ② 模型单元测试(pytest)                               │
│    │  通过?                                            │
│    ├─ 否 → 阻断                                        │
│    ▼ 是                                                │
│  ③ Golden Set回归测试(pytest + baseline对比)           │
│    │  通过?                                            │
│    ├─ 否 → 阻断,生成差异报告                            │
│    ▼ 是                                                │
│  ④ 鲁棒性/安全测试                                      │
│    │  通过?                                            │
│    ├─ 否 → 告警(不一定阻断)                            │
│    ▼ 是                                                │
│  ⑤ DeepChecks全面体检                                   │
│    │                                                    │
│    ▼                                                   │
│  ⑥ 生成测试报告 → 审批 → 部署                           │
└──────────────────────────────────────────────────────┘

5.2 GitHub Actions 配置示例

# .github/workflows/model-test.yml
name: Model Testing Pipeline

on:
  push:
    paths:
      - 'model/**'
      - 'tests/**'
      - 'data/**'
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Install Dependencies
        run: pip install -r requirements.txt
      
      - name: Run Unit Tests
        run: pytest tests/test_unit.py -v --junitxml=unit-results.xml
      
      - name: Run Golden Set Tests
        run: pytest tests/test_integration.py -v --junitxml=integration-results.xml
      
      - name: Run Robustness Tests
        run: pytest tests/test_robustness.py -v --junitxml=robustness-results.xml
      
      - name: Generate Report
        if: always()
        uses: dorny/test-reporter@v1
        with:
          name: Model Test Results
          path: '*.xml'
          reporter: java-junit
      
      - name: DeepChecks Evaluation
        run: python scripts/run_deepchecks.py

六、考试重点总结

6.1 工具辨析

工具 一句话定位 适用场景
pytest Python通用测试框架 所有模型的基础测试
DeepChecks 一键模型体检 传统ML/DL模型全面评估
Great Expectations 数据质量守门人 数据Pipeline验证
LangSmith LLM应用调试平台 Prompt测试、Chain追踪
Promptfoo Prompt评估工具 多模型/多Prompt对比

6.2 高频选择题

Q: 以下哪个工具最适合做LLM的Prompt测试?
A: LangSmith 或 Promptfoo ✅

Q: DeepChecks 的核心功能是?
A: 自动化模型评估,检测数据漂移、弱子群体等问题 ✅

Q: CI/CD中,模型回归测试应在哪个阶段运行?
A: 每次代码/模型/数据变更后自动触发 ✅

Q: Golden Set的作用是?
A: 作为基线数据集,对比当前模型输出检测退化 ✅

Q: pytest 中 @pytest.mark.parametrize 的作用是?
A: 参数化测试,用不同输入跑同一测试逻辑 ✅

七、思维导图

自动化测试框架与工具

为何自动化

效率提升

覆盖面广

CI/CD集成

可追溯

框架选型

pytest通用测试

DeepChecks模型体检

Great Expectations数据质量

LangSmith LLM调试

Promptfoo Prompt评估

AI Verify公平安全

pytest实战

conftest共享fixtures

单元测试

Golden Set回归测试

参数化鲁棒性测试

HTML报告生成

CI/CD集成

数据验证

模型单元测试

Golden Set回归

鲁棒性测试

DeepChecks体检

自动阻断与告警


📌 备考贴士:工具辨析题是重点,记住每个工具的"一句话定位"即可。pytest 是所有测试的基础框架必须掌握,DeepChecks 做模型全面评估是近年新考点,LangSmith/Promptfoo 是 LLM 时代的新工具。CI/CD 集成流程图也常考简答题。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐