共计 1779 个字符,预计需要花费 5 分钟才能阅读完成。
为什么我的测试用例总在半夜崩溃?
刚开始写 skill 测试用例时,我经常遇到这些场景:

- 修改一个请求参数,需要手动修改 20 个重复测试方法
- 凌晨 3 点 CI 突然报警,发现是因为测试数据被上个用例篡改
- 明明线上出过故障的异常分支,测试用例里居然没有覆盖
这些问题背后,往往是因为我们忽略了测试代码的工程化设计。下面分享一套经过实战检验的解决方案。
三层架构:让测试代码也能优雅成长
1. 用例层(TestCase)
负责测试逻辑描述,就像产品需求文档。这里应该只有测试场景描述和断言,所有的具体实现都下沉到其他层。
2. 数据层(TestData)
用 YAML 管理测试数据比硬编码更灵活,例如:
# tests/data/weather_skill.yaml
normal_case:
input: "上海天气"
expected: "上海今天晴转多云"
error_case:
input: "12345 天气"
expected: "请输入有效城市名"
3. 工具层(TestUtils)
封装公共操作,比如:
# tests/utils/api_mocker.py
def mock_weather_api(city, response):
patcher = unittest.mock.patch('weather.get_forecast')
mock_get = patcher.start()
mock_get.return_value = response
return patcher
数据驱动:用 Excel 思维写测试
unittest 的 subTest+ 参数化比复制粘贴香多了:
class TestWeatherSkill(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.test_data = load_yaml('weather_skill.yaml') # 加载所有测试数据
def test_weather_scenarios(self):
for case_name, data in self.test_data.items():
with self.subTest(case=case_name):
# 每个子用例独立运行
result = process_user_input(data['input'])
self.assertEqual(result, data['expected'])
异常测试:给你的代码挖坑
用 unittest.mock 模拟各种异常情况:
def test_api_timeout(self):
# 让接口总是超时
with unittest.mock.patch('requests.get',
side_effect=requests.Timeout) as mock_get:
response = fetch_remote_data()
self.assertIsNone(response)
self.assertEqual(mock_get.call_count, 3) # 验证重试逻辑
这些坑我帮你踩过了
1. 测试数据隔离
每次测试前重置数据库状态:
def setUp(self):
self._clean_db()
self.addCleanup(self._clean_db) # tearDown 时也会执行
2. 异步操作测试
用事件循环确保异步任务完成:
async def test_async_operation(self):
task = asyncio.create_task(async_process())
await asyncio.wait_for(task, timeout=1.0)
self.assertTrue(task.done())
3. CI 环境适配
在 pipeline 中加入缓存清理:
# .gitlab-ci.yml
test_job:
script:
- find . -name '__pycache__' -exec rm -rf {} +
- python -m pytest
进阶挑战:让你的测试更专业
- 用 pytest 重写用例,体验更简洁的 fixture 系统
- 生成 HTML 覆盖率报告:
pytest-cov --cov-report html - 尝试属性测试(hypothesis 库)自动生成边界用例
最后提醒:好的测试用例应该像侦探小说,明确交代犯罪现场(测试数据)、作案手法(测试步骤)、和破案关键(断言)。你现在准备好写出你的测试推理小说了吗?
正文完
