同一条 AI 应用测试昨天通过、今天失败,原因可能只是外部模型响应变了、工具 API 超时,或测试账户额度不足。Mock 的价值不是假装外部服务永远正确,而是让本地逻辑的成功、超时和坏响应可以稳定重现。本文给出一个只依赖 Python 标准库的最小例子。

适合读者:已有 HTTP/API 调用和单元测试经验,正在为模型、工具或支付接口写测试的开发者。示例使用 Python 3 的 unittest。

Why:外部波动不该掩盖本地缺陷

如果每个单元测试都访问真实模型,失败时很难判断是业务代码、网络、配额还是模型输出变化。反过来,如果所有测试都只用 Mock,又可能漏掉真实 API 的字段、认证和限流变化。两类测试回答的问题不同:单元测试保证自己的分支逻辑,契约测试保证与外部服务的接口仍兼容。

What:在明确边界处替换依赖

业务函数 → client.complete() → 外部模型/API
    │            │
    └─ 单元测试注入 FakeClient:成功 / 超时 / 坏响应
                 └─ 独立契约测试:少量真实请求核对字段

Mock 应放在自己控制的客户端接口上,而不是把整段业务逻辑一并替换。录制回放可保存经过脱敏的响应样本,用于重现复杂结构;样本需标注 API 版本和采集条件,并定期用真实契约测试校对。不要把密钥、用户提示词或支付信息原样提交到仓库。

How:三种结果都能稳定复现

保存为 test_classifier.py,运行 python3 -m unittest -q test_classifier.py。下面的 FakeClient 不访问网络,三项测试分别覆盖正常响应、超时和无效字段。

import unittest

def classify(client, text, timeout_s):
    try:
        payload = client.complete(text, timeout=timeout_s)
    except TimeoutError:
        return "retry"
    if not isinstance(payload, dict) or payload.get("label") not in {"ok", "review"}:
        raise ValueError("模型响应缺少有效 label")
    return payload["label"]

class FakeClient:
    def __init__(self, result=None, error=None):
        self.result, self.error = result, error
    def complete(self, text, timeout):
        if self.error:
            raise self.error
        return self.result

class ClassifierTest(unittest.TestCase):
    def test_success(self):
        self.assertEqual(classify(FakeClient({"label": "ok"}), "sample", 1), "ok")
    def test_timeout(self):
        self.assertEqual(classify(FakeClient(error=TimeoutError()), "sample", 1), "retry")
    def test_bad_response(self):
        with self.assertRaises(ValueError):
            classify(FakeClient({"label": "unknown"}), "sample", 1)

if __name__ == "__main__":
    unittest.main()

示例中的超时参数 1 只是测试输入,不是生产推荐值。真实系统应根据接口预算和用户体验决定超时、重试与降级策略;重试还要考虑幂等性,尤其是支付或其他写入动作。

接入 CI 时别漏掉这些坑

  1. 单元测试每次提交运行,固定输入与断言;不要依赖模型“可能说出某句话”。
  2. 对超时、限流、服务错误、字段缺失分别注入故障,核对重试或降级是否符合产品契约。
  3. 用独立的真实服务契约测试检查认证、请求字段和响应结构,并将凭据留在安全的 CI 配置中。
  4. 录制回放样本先脱敏,明确过期与更新方式;Mock 与真实服务出现差异时以真实接口为准。

结论:Mock 负责稳定地证明你的代码如何处理已知情况,真实契约测试负责发现接口漂移。延伸阅读:Python unittest.mock 文档、pytest 文档。

EOF · 全文完 未分类 · · 共 861 字

感谢耐心读到这里。若这篇文字曾带来片刻启发或共鸣,不妨点亮喜欢或分享给同路人。

喜欢 0
DISCUSSION / 读者评论 0 条评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注