跳到主要内容

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测试前缀
});

最佳实践

  1. 命名约定:使用清晰的函数名前缀和描述性的名称
  2. 数据提供者:保持数据提供者函数简单,返回明确的测试数据
  3. 基准测试:确保基准测试的代码能够代表实际的性能场景
  4. 测试组织:将相关的测试放在同一个文件中,使用注释分组
  5. 参数命名:使用有意义的参数名,便于理解测试意图