在PA2中学到的有关编写测试用例的经验补充
来源:ysyx5期, PA2 基础设施(2)- “测试你的klib”
- 内存和字符串的写入函数, 例如
memset()
,strcpy()
等. - 内存和字符串的只读函数, 例如
memcmp()
,strlen()
等. - 格式化输出函数, 例如
sprintf()
等.
如何进行充分的测试:
1. 在可以遍历输入范围时,遍历输入范围
针对第一类函数, 我们应该如何构造一个测试场景, 使得存在一些方法来容易地得到测试输出呢? 注意这些函数都是对一个内存区域进行写入, 考虑如下的数组:
#define N 32
uint8_t data[N];
void reset() {
int i;
for (i = 0; i < N; i ++) {
data[i] = i + 1;
}
}
这样的一个数组, 每个元素都是1个字节, 而且它们的值都各不相同. 如果我们在这个数组上进行测试, 只要实际输出有1个字节不正确, 都可以大概率被检查出来. 为了得到预期的输出, 我们还要思考测试函数的预期行为: 以上函数都是对数组中的一段连续区间进行写入, 于是我们可以把预期的输出分成三段来检查:
- 第一段是函数写入区间的左侧, 这一段区间没有被写入, 因此应该有
assert(data[i] == i + 1)
- 第二段是函数写入的区间本身, 这一段区间的预期结果和函数的具体行为有关
- 第三段是函数写入区间的右侧, 这一段区间没有被写入, 因此应该有
assert(data[i] == i + 1)
于是我们可以编写两个辅助函数用于检查:
// 检查[l,r)区间中的值是否依次为val, val + 1, val + 2...
void check_seq(int l, int r, int val) {
int i;
for (i = l; i < r; i ++) {
assert(data[i] == val + i - l);
}
}
// 检查[l,r)区间中的值是否均为val
void check_eq(int l, int r, int val) {
int i;
for (i = l; i < r; i ++) {
assert(data[i] == val);
}
}
有了这两个函数, 我们就可以遍历各种输入, 并且很容易地编写出测试函数的预期输出了. 例如针对memset()
, 我们可以编写如下的测试代码:
void test_memset() {
int l, r;
for (l = 0; l < N; l ++) {
for (r = l + 1; r <= N; r ++) {
reset();
uint8_t val = (l + r) / 2;
memset(data + l, val, r - l);
check_seq(0, l, 1);
check_eq(l, r, val);
check_seq(r, N, r + 1);
}
}
}
2. 在输入范围过大时,我们选取有代表性的输入
最后我们来看格式化输出函数. 以%d
为例, 我们需要构造一些输入. 但整数的范围太大了, 不能全部遍历它们, 因此我们需要挑选一些有代表性的整数. limits.h
这个C标准头文件里面包含了一些最大数和最小数的定义, 你可以打开/usr/include/limits.h
来阅读它们. 一些有代表性的整数可以是:
int data[] = {0, INT_MAX / 17, INT_MAX, INT_MIN, INT_MIN + 1,
UINT_MAX / 17, INT_MAX / 17, UINT_MAX};
为了得到相应的预期输出, 我们可以先编写一个native程序来用printf输出它们, 然后把输出结果整理到测试代码里面. cpu-tests
中的预期输出也是这样生成的.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?