XUnit 命名约定测试指南
概述
XUnit 测试框架2.0版本支持通过函数名前缀来隐性识别测试用例,无需显式调用装饰器函数进行注册。这种方式更加简洁和自然,类似于其他主流测试框架的约定。
新版本特性
🎯 核心改进
- 简化的参数注入:仅支持数据提供者函数方式,移除了其他复杂的注入方案
- 基准测试支持:新增 Benchmark 测试类型,用于性能测试
- 更好的命名约定:支持更多的函数名前缀模式
- 删除装饰器:移除了
Fact()
和Theory()
装饰器,完全基于命名约定
🚀 自动测试发现
- 基于函数名前缀自动识别测试用例
- 支持多种命名约定
- 自动解析测试元数据(跳过、超时等)
测试类型
1. Fact 测试
用于单个断言的简单测试,不需要参数。
支持的前缀:
test_
- 传统的测试前缀fact_
- 明确的事实测试should_
- BDD 风格的测试
示例:
public void test_simple_calculation()
{
int result = 2 + 3;
xassert.equal(result, 5, "计算结果应该正确");
}
public void should_validate_email_format()
{
bool result = validate_email("test@example.com");
xassert.is_true(result, "邮箱格式应该有效");
}
2. Theory 测试
用于参数化测试,需要使用数据提供者函数提供测试数据。
支持的前缀:
theory_
- 理论测试前缀param_test_
- 参数化测试前缀
示例:
public void theory_calculator_operations(int a, int b, int expected)
{
int result = a + b;
xassert.equal(result, expected, sprintf("%d + %d 应该等于 %d", a, b, expected));
}
// 数据提供者函数:测试函数名 + "_data"
public array theory_calculator_operations_data()
{
return [
[ 1, 1, 2 ],
[ 2, 3, 5 ],
[ 10, 20, 30 ],
[ -5, 5, 0 ]
];
}
3. Benchmark 测试(新增)
用于性能基准测试,自动统计执行时间和性能 指标。
支持的前缀:
bench_
- 基准测试前缀benchmark_
- 完整的基准测试前缀perf_
- 性能测试前缀
示例:
public void bench_string_concat()
{
// 测试字符串连接性能
string result = "";
for (int i = 0; i < 100; i++)
{
result += "test";
}
}
public void benchmark_array_operations()
{
// 测试数组操作性 能
array arr = [];
for (int i = 0; i < 1000; i++)
{
arr.push_back(i);
}
}
测试元数据
跳过测试
在函数名中包含 _skip_
标记:
public void test_skip_incomplete_feature()
{
// 这个测试会被自动跳过
}
设置超时时间
在函数名中包含 _timeout_数字_
标记:
public void test_timeout_30_slow_operation()
{
// 这个测试有30 秒的超时时间
}
基准测试配置
设置迭代次数: 在函数名中包含 _iter_数字_
标记
public void perf_iter_5000_map_operations()
{
// 这个基准测试会运行5000次迭代
}
设置预热次数: 在函数名中包含 _warmup_数字_
标记
public void bench_warmup_200_function_call()
{
// 这个基准测试会有200次预热
}
同时设置迭代和预热次数:
public void benchmark_iter_2000_warmup_50_sorting()
{
// 2000次迭代,50次预热
}
参数注入方案
数据提供者函数(唯一支持的方式)
创建一个与测试函数同名的函数,并在后面加上 _data
后缀:
// 测试函数
public void theory_test_string_operations(string input, string expected)
{
string result = input.upper();
xassert.equal(result, expected, sprintf("'%s' 转大写应该是 '%s'", input, expected));
}
// 数据提供者函数
public array theory_test_string_operations_data()
{
return [
[ "hello", "HELLO" ],
[ "world", "WORLD" ],
[ "test", "TEST" ],
[ "example", "EXAMPLE" ]
];
}
数据提供者函数规则:
- 函数名必须是:
测试函数名
+_data
- 必须返回一个二维数组
- 数组的每个元素都是一组测试参数
- 参数顺序必须与测试函数的参数顺序一致
使用方式
自动发现测试
private void create()
{
// 自动发现并注册所有测试用例
int discovered_count = xunit.discover_tests_by_naming_convention(this_object());
printf("发现了 %d 个测试用例\n", discovered_count);
}
运行测试
// 运行所有测试(包括基准测试)
map report = xunit.run_all_tests({});
// 只运行基准测试
map benchmark_report = xunit.run_benchmark_tests({});
// 运行指定的测试
array test_names = [ "test_simple_calculation", "theory_calculator_operations" ];
map report = xunit.run_tests(test_names);
命令行参数
基本参数:
--verbose
或-v
- 详细输出--parallel
或-p
- 并行执行--timeout
或-t
- 设置超时时间--filter
- 过滤测试用例
基准测试参数:
--benchmark-only
或-b
- 只运行基准测试--benchmark-iterations
- 设置默认迭代次数--benchmark-warmup
- 设置默认预热次数
示例:
# 运行所有测试,启用详细输出
gs xunit.gs --verbose
# 只运行基准测试
gs xunit.gs --benchmark-only
# 设置基准测试参数
gs xunit.gs --benchmark-iterations 2000 --benchmark-warmup 100
# 过滤特定测试
gs xunit.gs --filter "test_simple_calculation,theory_calculator_operations"
基准测试报告
基准测试会自动生成详细的性能报告:
=== 基准测试报告 ===
总计基准测试: 5
成功: 5, 失败: 0
测试名称 平均时间(s) 最小时间(s) 最大时间(s) 操作/秒
------------------------------ ------------ ------------ ------------ ------------
bench_string_concat 0.000012 0.000010 0.000015 83333.33
benchmark_array_operations 0.000008 0.000007 0.000010 125000.00
perf_iter_5000_map_operations 0.000006 0.000005 0.000008 166666.67
bench_warmup_200_function_call 0.000003 0.000002 0.000004 333333.33
benchmark_iter_2000_warmup_50_sorting 0.000015 0.000012 0.000018 66666.67
配置选项
// 配置测试框架
xunit.configure({
"verbose": true, // 详细输出
"parallel": false, // 并行执行
"timeout": 30, // 默认超时时间
"benchmark_iterations": 1000, // 默认基准测试迭代次数
"benchmark_warmup": 100, // 默认基准测试预热次数
"fact_prefixes": [ "test_", "fact_", "should_" ], // Fact测试前缀
"theory_prefixes": [ "theory_", "param_test_" ], // Theory测试前缀
"benchmark_prefixes": [ "bench_", "benchmark_", "perf_" ], // Benchmark测试前缀
});
最佳实践
- 命名约定:使用清晰的函数名前缀和描述性的名称
- 数据提供者:保持数据提供者函数简单,返回明确的测试数据
- 基准测试:确保基准测试的代码能够代表实际的性能场景
- 测试组织:将相关的测试放在同一个文件中,使用注释分组
- 参数命名:使用有意义的参数名,便于理解测试意图