属性测试是什么
属性测试(property-based testing)不写具体输入输出,而是声明「对所有合法输入都应成立的性质」:排序结果有序、编解码互逆、操作幂等。框架自动生成成百上千组输入寻找反例,失败后自动收缩(shrink)到最小反例。示例测试测你想得到的边界,属性测试测你没想到的组合。
基本信息
| Python | hypothesis:@given 加 strategies 描述输入分布 |
|---|---|
| Java | jqwik 等支持属性与收缩 |
| JavaScript | fast-check 面向 JS/TS |
| 常见性质 | 不变量、互逆(编码解码)、幂等、参照模型对照 |
| 失败处理 | 自动收缩输出最小反例,附随机种子便于复现 |
| 状态机测试 | 对命令序列建模,验证状态流转的通用性质 |
最小示例
最小示例
from hypothesis import given, strategies as st
import json
@given(st.text())
def test_json_roundtrip(s): # 性质:编解码互逆
assert json.loads(json.dumps(s)) == s
@given(st.lists(st.integers()))
def test_sorted_is_ordered(xs): # 性质:结果有序
ys = sorted(xs)
assert all(a <= b for a, b in zip(ys, ys[1:]))典型使用场景
- 算法与工具函数:排序、解析、编解码的正确性验证
- 序列化兼容:旧版本代码能解析新版本数据
- 数值边界:自动覆盖极值、零、负数等组合
- 状态机:订单状态流转、会话管理的过程性验证
- 与示例测试组双层网:示例抓已知场景,属性扫未知区域
四个概念怎么理解
| 概念 | 含义 | 注意 |
|---|---|---|
| property | 对所有合法输入成立的性质 | 写错会误报或漏报 |
| strategy | 描述输入如何生成 | 约束过宽会产出无效用例 |
| shrink | 失败后收缩到最小反例 | 最小反例是修 bug 的入口 |
| seed | 随机种子 | 固定种子复现,定期放开探索 |
性质要先拿已知实现验证再上线:性质写错(过强)会让测试常红,浪费时间;写弱(接近恒真)则测不出东西。
生成数据用约束贴近业务分布:纯随机整数常远离业务范围,加最小值、最大值与结构化策略。
失败后把最小反例固化成示例用例再修代码:否则修完没有回归防线,同类问题可能再次溜进来。