Hypothesis:让测试自己找 Bug 的 Python 库

hypothesis 在 GitHub 上拿到了 8,659 个 Star。

这个 Python 库的思路很简单:你不用再绞尽脑汁想测试用例了。声明输入的数据结构和输出应该满足的性质,Hypothesis 自动生成海量随机数据来验证,包括你压根没想到的边界情况。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1、 传统测试的盲区

手写测试用例的质量完全取决于写的人。给加法函数写测试,你大概率会写 assert add(1, 2) == 3。测过了,提交了,上线了。然后某天来了个 add(INT_MAX, 1) 直接溢出。

这不是编的故事。每个开发者都遇到过:测试全绿,线上炸了。因为人天然会写"自己觉得正常"的输入,而 Bug 刚好藏在那些你觉得不正常的角落里。空列表、负数、零、极大值、重复元素、Unicode 字符……每多一个维度,手写用例的组合爆炸就加剧一分。

2、 property-based testing 怎么解决问题

Hypothesis 换了一种思路。你声明的是性质而非用例:对所有合法输入,函数应该始终满足哪些不变式。

拿排序函数举例。性质有三条:排序后长度不变、元素按升序排列、包含原列表全部元素。Hypothesis 拿到这三条性质后,自动生成各种列表往上套:空的、单个元素的、全是重复值的、已经排好序的、恰好反序的、极大的、极小的。哪个性质不满足,哪个就是 Bug。

这个策略的威力在于,每次运行生成的测试数据都不一样。今天测 100 组,明天再测 100 组完全不重复的新数据。测试覆盖率随时间自动增长。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

比随机生成更厉害的是 shrinking。一旦发现失败的输入,Hypothesis 不会把原始数据直接丢给你,而是自动化简到最小可复现用例。比如你写了个有去重 Bug 的排序 sorted(set(ls)),它不报一长串杂乱数字让你排查半天,而是精确告诉你:[0, 0] 就能触发。两个零,一目了然,Bug 是去重逻辑把重复元素吞掉了。

3、 十分钟上手

pip install hypothesis

写第一个 property-based 测试只需要一个装饰器:

from hypothesis import given, strategies as st

@given(st.lists(st.integers()))
def test_my_sort_matches_builtin(ls):
    assert sorted(ls) == my_sort(ls)

st.lists(st.integers()) 定义了输入策略:任意长度的整数列表。Hypothesis 自带几十种内置策略,整数、浮点数、字符串、字典、日期、甚至自定义复杂对象,组合起来能描述几乎所有输入空间。

4、 适合哪些人

  • 写数据处理管线的开发者:输入格式多变,手写用例永远不够

  • 做序列化和反序列化的场景:protobuf、JSON Schema、ORM 映射,来回转换最怕边界值

  • 涉及数学计算的功能:溢出、精度丢失、除零,人工覆盖成本太高

  • 任何你写到一半心里嘀咕"这里应该不会出问题吧"的时刻,Hypothesis 会给你一个确定的答案

  • 任何你写到一半心里嘀咕"这里应该不会出问题吧"的时刻,Hypothesis 会给你一个确定的答案

更多推荐