Skip to content

Codelab: 参数化边界测试

预计时间:15 分钟

学习目标

完成本教程后,你将能够:

  • 使用边界类型自动生成测试用例
  • 使用组合策略减少测试数量
  • 使用显式用例精确控制测试

场景描述

你正在开发一个数据验证库,需要测试各种边界情况:

  • 字节范围验证(0-255)
  • 坐标边界检查
  • 数据类型转换

手动编写每个边界测试太繁琐了。

解决方案:使用参数化测试自动生成用例。

步骤 1: 创建项目结构

bash
mkdir -p validator-demo/{src,include,build,tests}
cd validator-demo

步骤 2: 编写验证函数

创建 include/validator.h

c
#ifndef VALIDATOR_H
#define VALIDATOR_H

// 验证字节值(0-255有效)
int is_valid_byte(int value);

// 验证坐标(0到max-1有效)
int is_in_bounds(int x, int y, int max_x, int max_y);

// 截断到字节范围
int clamp_byte(int value);

// 标准化布尔值
int normalize_bool(int value);

#endif

创建 src/validator.c

c
#include "validator.h"

int is_valid_byte(int value) {
    return (value >= 0 && value <= 255) ? 1 : 0;
}

int is_in_bounds(int x, int y, int max_x, int max_y) {
    return (x >= 0 && x < max_x && y >= 0 && y < max_y) ? 1 : 0;
}

int clamp_byte(int value) {
    if (value < 0) return 0;
    if (value > 255) return 255;
    return value;
}

int normalize_bool(int value) {
    return value != 0 ? 1 : 0;
}

步骤 3: 编译源代码

bash
gcc -c src/validator.c -o build/validator.o -Iinclude

步骤 4: 使用边界类型

创建 tests/test_validator.c

c
#include "validator.h"

/*
 * 设计说明:使用 uint8 边界值测试字节验证
 * 预期结果:0-255有效,-1和256无效
 */
__attribute__((test_method, params(n = boundary(uint8))))
int test_valid_byte_boundary(int n) {
    int result = is_valid_byte(n);
    int expected = (n >= 0 && n <= 255) ? 1 : 0;
    return result == expected;
}

// includes: -I../include

运行测试:

bash
anvil

输出:

Discovered 8 test cases in 1 file.
test_validator::test_valid_byte_boundary[n=0] ...... PASSED
test_validator::test_valid_byte_boundary[n=1] ...... PASSED
test_validator::test_valid_byte_boundary[n=127] .... PASSED
test_validator::test_valid_byte_boundary[n=128] .... PASSED
test_validator::test_valid_byte_boundary[n=254] .... PASSED
test_validator::test_valid_byte_boundary[n=255] .... PASSED
test_validator::test_valid_byte_boundary[n=-1] ..... PASSED
test_validator::test_valid_byte_boundary[n=256] .... PASSED

All 8 tests passed.

一个函数,自动生成 8 个测试用例!

步骤 5: 使用显式值列表

tests/test_validator.c 中添加:

c
/*
 * 设计说明:使用自定义值列表测试
 * 预期结果:涵盖典型边界点
 */
__attribute__((test_method, params(value = {-100, -1, 0, 1, 127, 128, 254, 255, 256, 1000})))
int test_valid_byte_explicit(int value) {
    int result = is_valid_byte(value);
    int expected = (value >= 0 && value <= 255) ? 1 : 0;
    return result == expected;
}

步骤 6: 多参数笛卡尔积

c
/*
 * 设计说明:测试二维坐标验证(笛卡尔积)
 * 预期结果:坐标在 [0, 100) 范围内有效
 */
__attribute__((test_method,
    params(x = {-1, 0, 50, 99, 100}, y = {-1, 0, 50, 99, 100})))
int test_bounds_cartesian(int x, int y) {
    int result = is_in_bounds(x, y, 100, 100);
    int expected = (x >= 0 && x < 100 && y >= 0 && y < 100) ? 1 : 0;
    return result == expected;
}

这会生成 5×5=25 个测试用例。

步骤 7: 使用 Pairwise 策略

c
/*
 * 设计说明:使用 Pairwise 减少组合数量
 * 预期结果:覆盖所有两两组合
 */
__attribute__((test_method,
    params(x = {-1, 0, 50, 99, 100}, y = {-1, 0, 50, 99, 100}),
    strategy(pairwise)))
int test_bounds_pairwise(int x, int y) {
    int result = is_in_bounds(x, y, 100, 100);
    int expected = (x >= 0 && x < 100 && y >= 0 && y < 100) ? 1 : 0;
    return result == expected;
}

Pairwise 策略会显著减少测试用例数量。

步骤 8: 使用 Zip 策略

c
/*
 * 设计说明:使用 Zip 测试截断函数
 * 预期结果:输入和预期输出一一对应
 */
__attribute__((test_method,
    params(input = {-100, -1, 0, 128, 255, 256, 1000},
           expected = {0, 0, 0, 128, 255, 255, 255}),
    strategy(zip)))
int test_clamp_byte_zip(int input, int expected) {
    return clamp_byte(input) == expected;
}

生成 7 个测试用例,每个输入对应一个预期输出。

步骤 9: 使用 cases() 语法

c
/*
 * 设计说明:使用 cases 语法精确控制用例
 * 预期结果:每个用例独立验证
 */
__attribute__((test_method, cases(
    (-100, 0),
    (-1, 0),
    (0, 0),
    (128, 128),
    (255, 255),
    (256, 255),
    (1000, 255)
)))
int test_clamp_byte_cases(int input, int expected) {
    return clamp_byte(input) == expected;
}

步骤 10: 测试布尔边界

c
/*
 * 设计说明:测试布尔标准化的边界值
 * 预期结果:0->0, 非零->1
 */
__attribute__((test_method, params(value = boundary(bool))))
int test_normalize_bool_boundary(int value) {
    int result = normalize_bool(value);
    int expected = (value != 0) ? 1 : 0;
    return result == expected;
}

步骤 11: 运行所有测试

bash
anvil

查看生成的测试用例数量和结果。

知识点总结

你已学会:

技能说明
boundary(type)自动生成边界值
params(x = {...})显式值列表
笛卡尔积默认策略,所有组合
strategy(pairwise)减少组合数量
strategy(zip)按索引配对
cases((...), ...)精确控制每个用例

策略选择指南

场景推荐策略
单参数边界测试boundary(type)
2个参数,值少笛卡尔积(默认)
3+个参数strategy(pairwise)
输入/输出配对strategy(zip)cases()

完整测试文件

最终的 tests/test_validator.c

c
#include "validator.h"

// 边界类型测试
__attribute__((test_method, params(n = boundary(uint8))))
int test_valid_byte_boundary(int n) {
    int result = is_valid_byte(n);
    int expected = (n >= 0 && n <= 255) ? 1 : 0;
    return result == expected;
}

// 显式值列表
__attribute__((test_method, params(value = {-100, -1, 0, 1, 127, 128, 254, 255, 256, 1000})))
int test_valid_byte_explicit(int value) {
    int result = is_valid_byte(value);
    int expected = (value >= 0 && value <= 255) ? 1 : 0;
    return result == expected;
}

// 笛卡尔积
__attribute__((test_method,
    params(x = {-1, 0, 50, 99, 100}, y = {-1, 0, 50, 99, 100})))
int test_bounds_cartesian(int x, int y) {
    int result = is_in_bounds(x, y, 100, 100);
    int expected = (x >= 0 && x < 100 && y >= 0 && y < 100) ? 1 : 0;
    return result == expected;
}

// Pairwise 策略
__attribute__((test_method,
    params(x = {-1, 0, 50, 99, 100}, y = {-1, 0, 50, 99, 100}),
    strategy(pairwise)))
int test_bounds_pairwise(int x, int y) {
    int result = is_in_bounds(x, y, 100, 100);
    int expected = (x >= 0 && x < 100 && y >= 0 && y < 100) ? 1 : 0;
    return result == expected;
}

// Zip 策略
__attribute__((test_method,
    params(input = {-100, -1, 0, 128, 255, 256, 1000},
           expected = {0, 0, 0, 128, 255, 255, 255}),
    strategy(zip)))
int test_clamp_byte_zip(int input, int expected) {
    return clamp_byte(input) == expected;
}

// cases 语法
__attribute__((test_method, cases(
    (-100, 0), (-1, 0), (0, 0), (128, 128), (255, 255), (256, 255), (1000, 255)
)))
int test_clamp_byte_cases(int input, int expected) {
    return clamp_byte(input) == expected;
}

// 布尔边界
__attribute__((test_method, params(value = boundary(bool))))
int test_normalize_bool_boundary(int value) {
    int result = normalize_bool(value);
    int expected = (value != 0) ? 1 : 0;
    return result == expected;
}

// includes: -I../include

完成所有 Codelab

恭喜!你已掌握 Anvil 的核心功能:

  • 基本测试编写
  • Mock 隔离测试
  • 参数化边界测试

现在可以开始在你的项目中使用 Anvil 了!

继续学习

本页面内容遵循 Luna 软件源代码授权条款 (LSLA) 发布