关于 property-based-testing
为开发者提供基于生成输入验证性质与不变量的测试参考,而非只检查固定示例。内容涵盖 TypeScript/JavaScript 的 fast-check 和 Python 的 Hypothesis,包括缩减失败用例与状态测试。
主要功能
- 01生成测试输入
- 02缩减失败用例
- 03fast-check 与 Hypothesis 示例
- 04性质与不变量测试模式
- 05使用 Hypothesis 进行状态测试
适用场景
- 01检查编码与解码往返
- 02发现数据转换的边界情况
- 03用生成数据验证 API 契约
- 04针对不变量测试操作序列
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ laurigates/ claude-plugins --skill property-based-testing安装后在 Claude Code 中输入/property-based-testing确认已加载。
附带文件REFERENCE.md
详细介绍
基于属性的测试
基于属性的测试专业知识——自动生成测试用例,以验证代码属性,而非测试特定示例。
何时使用此技能
| 在以下情况使用此技能…… | 在以下情况改用其他技能…… |
|---|---|
| 测试数学属性(交换律、结合律) | 编写基于特定示例的单元测试 |
| 测试编码/解码的往返过程 | 设置测试运行器配置 |
| 自动查找边界情况 | 进行端到端浏览器测试 |
| 验证数据转换和不变量 | 分析测试质量或异味 |
| 使用生成的数据测试 API 契约 | 运行变异测试 |
核心专业知识
基于属性的测试概念
- 传统测试:测试特定示例
- 基于属性的测试:测试应对所有输入成立的属性
- 生成器:自动创建多样化的测试输入
- 缩减:将失败用例缩减为最简单的示例
- 覆盖率:探索人们可能忽略的边界情况
何时使用基于属性的测试
- 数学运算(交换律、结合律等属性)
- 编码器/解码器(往返属性)
- 解析器和序列化器
- 数据转换
- API 契约
- 不变量和约束
TypeScript/JavaScript(fast-check)
安装
# Using Bun
bun add -d fast-check
# Using npm
npm install -D fast-check
基本示例
import { test } from 'vitest'
import * as fc from 'fast-check'
// Property-based test
test('reverse twice returns original - property based', () => {
fc.assert(
fc.property(
fc.array(fc.integer()), // Generate random arrays of integers
(arr) => {
expect(reverse(reverse(arr))).toEqual(arr)
}
)
)
})
// fast-check automatically generates 100s of test cases!
关键生成器(速查)
| 生成器 | 描述 |
|---|---|
fc.integer() | 任意整数(可选最小值/最大值) |
fc.nat() | 自然数(>= 0) |
fc.float() / fc.double() | 浮点数 |
fc.string() | 任意字符串(可选长度) |
fc.emailAddress() | 符合电子邮件格式的字符串 |
fc.array(arb) | 任意类型的数组 |
fc.record({...}) | 含有类型化字段的对象 |
fc.boolean() | 布尔值 |
fc.constantFrom(...) | 从选项中选取 |
fc.tuple(...) | 固定大小的元组 |
fc.oneof(...) | 联合类型 |
fc.option(arb) | 值或 null |
fc.date() | 日期对象 |
常见测试属性
| 属性 | 模式 | 示例 |
|---|---|---|
| 往返 | f(g(x)) = x | 编码/解码、序列化/解析 |
| 幂等性 | f(f(x)) = f(x) | 排序、规范化、格式化 |
| 交换律 | f(a,b) = f(b,a) | 加法、合并、并集 |
| 结合律 | f(f(a,b),c) = f(a,f(b,c)) | 加法、连接 |
| 单位元 | f(x, id) = x | 乘以 1、加 0 |
| 逆运算 | f(g(x)) = x | 加密/解密 |
配置
fc.assert(property, {
numRuns: 1000, // Run 1000 tests (default: 100)
seed: 42, // Reproducible tests
endOnFailure: true, // Stop after first failure
})
前置条件
fc.pre(b !== 0) // Skip cases where b is 0
Python(Hypothesis)
安装
# Using uv
uv add --dev hypothesis pytest
# Optional extensions
uv add --dev hypothesis[numpy] # NumPy strategies
uv add --dev hypothesis[django] # Django model strategies
配置
# pyproject.toml
[tool.hypothesis]
max_examples = 200
deadline = 1000
[tool.hypothesis.profiles.dev]
max_examples = 50
deadline = 1000
[tool.hypothesis.profiles.ci]
max_examples = 500
deadline = 5000
verbosity = "verbose"
# Activate profile in conftest.py
from hypothesis import settings
settings.load_profile("ci")
基本示例
from hypothesis import given, example, assume
import hypothesis.strategies as st
# Test a property
@given(st.integers(), st.integers())
def test_addition_commutative(a, b):
assert a + b == b + a
# Add explicit edge cases with @example
@given(st.integers())
@example(0)
@example(-1)
@example(2**31 - 1)
def test_with_explicit_examples(x):
assert process(x) is not None
关键策略(速查)
| 策略 | 描述 |
|---|---|
st.integers() | 任意整数(可选边界) |
st.floats() | 浮点数 |
st.text() | 任意字符串(可选大小) |
st.binary() | 字节字符串 |
st.lists(strat) | 由给定策略生成的列表 |
st.sets(strat) | 唯一值集合 |
st.dictionaries(k, v) | 字典 |
st.booleans() | 布尔值 |
st.sampled_from(...) | 从选项中选取 |
st.tuples(...) | 固定大小的元组 |
st.one_of(...) | 联合类型 |
st.emails() | 有效电子邮件地址 |
st.uuids() | UUID 对象 |
st.dates() / st.datetimes() | 日期/时间值 |
st.builds(Class, ...) | 根据策略构建对象 |
配置(设置)
from hypothesis import given, settings, strategies as st
@settings(max_examples=1000, deadline=None)
@given(st.lists(st.integers()))
def test_with_custom_settings(arr):
assert sort(arr) == sorted(arr)
假设
from hypothesis import assume
assume(b != 0) # Skip cases where b is 0
有状态测试
Hypothesis 支持通过 RuleBasedStateMachine 进行有状态测试,用于针对不变量测试一系列操作。
CI 集成
# .github/workflows/test.yml
- name: Run hypothesis tests
run: |
uv run pytest \
--hypothesis-show-statistics \
--hypothesis-profile=ci \
--hypothesis-seed=${{ github.run_number }}
- name: Upload hypothesis database
uses: actions/upload-artifact@v4
if: failure()
with:
name: hypothesis-examples
path: .hypothesis/
速查
# Core decorators
@given(strategy) # Generate test inputs
@example(value) # Add explicit test case
@settings(max_examples=500) # Configure behavior
# Key helpers
assume(condition) # Skip invalid inputs
note(message) # Add debug info to failure output
target(value) # Guide generation toward value
面向 Agent 的优化
| 上下文 | 命令 |
|---|---|
| 快速 TS 测试 | bunx vitest --dots --bail=1 --grep 'property' |
| 快速 Python 测试 | uv run pytest -x -q --tb=short -k 'property' |
| CI TS 测试 | bunx vitest run --reporter=junit --grep 'property' |
| CI Python 测试 | uv run pytest --hypothesis-profile=ci --hypothesis-show-statistics -q |
| 可复现 | fc.assert(prop, { seed: 42 }) 或 pytest --hypothesis-seed=42 |
| 快速迭代 | fc.assert(prop, { numRuns: 50 }) 或 pytest --hypothesis-profile=dev -x |
| 调试失败项 | pytest -x -s --hypothesis-verbosity=debug |
| 不进行缩减 | 在 @settings 中添加 phases=[Phase.generate] |
如需详细示例、高级模式和最佳实践,请参阅 REFERENCE.md。
另请参阅
vitest-testing- 单元测试框架python-testing- Python pytest 测试test-quality-analysis- 检测测试异味mutation-testing- 验证测试有效性
参考资料
- fast-check:https://fast-check.dev/
- Hypothesis:https://hypothesis.readthedocs.io/
- 基于属性的测试:https://fsharpforfunandprofit.com/posts/property-based-testing/
评论
支持 Markdown