本文已参与「新人创作礼」活动,一起开启掘金创作之路。
单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。进行单元测试,可以尽早地发现编写代码中错误,减少后期测试开销和维护成本,提高软件质量。 下文讲解写出好单元测试需要遵守的FIRST原则(AIR原则与FIRST原则中的S、I、R项类似):
FIRST 原则
F——Fast:快速
在调试bug时,需要频繁去运行单元测试验证结果是否正确。如果单元测试足够快速,就可以省去不必要浪费的时间,提高工作效率。
I——Isolated:隔离
好的单元测试是每个测试只关注逻辑的一个方面,这样有利于排错。
// 按照逻辑分离测试
TEST(Func, NullPtr) {
/* 空指针测试内容 */
}
TEST(Func, BadParams) {
/* 错误参数测试内容 */
}
每个测试之间不应该产生依赖,不会因为测试顺序不同而导致运行的结果不同。
//! 错误示范 ❌
TEST(Func, NullPtr) {
char* str = create_str(); /* 创建字符串 */
/* 空指针测试内容 */
}
TEST(Func, BadParams) {
char* str = get_str(); /* 获取字符串 */
/* 错误参数测试内容 */
destroy_str(); /* 删除字符串 */
}
// 正确示范 ✔
TEST(Func, NullPtr) {
char* str = create_str(); /* 创建字符串 */
/* 空指针测试内容 */
destroy_str(); /* 删除字符串 */
}
TEST(Func, BadParams) {
char* str = create_str(); /* 创建字符串 */
/* 错误参数测试内容 */
destroy_str(); /* 删除字符串 */
}
测试时不要依赖和修改外部数据等其他共享资源,做到测试前后共享资源数据一致。
//! 错误示范 ❌
TEST(Func, NullPtr) {
/* 向文件写字符串 */
FILE* fp=fopen("TEST.txt","w");
/* 原文本内容为 "BadParams\n"*/
fprintf(fp, "NullPtr\n");
/* 空指针测试内容 */
fclose(fp);
}
TEST(Func, BadParams) {
/* 读文件字符串 */
FILE* fp=fopen("TEST.txt","r");
char str[64];
memset(str,'\0',sizeof(str));
fscanf(fp, "%s\n", str);
ASSERT_STREQ(str, "BadParams"); /*! error❌ */
/* 错误参数测试内容 */
fclose(fp);
}
R——Repeatable:可重复
单元测试需要保持运行稳定,每次运行都需要得到同样的结果,如果间歇性的失败,会导致我们不断的去查看这个测试,不可靠的测试也就失去了意义。
//! 错误示范 ❌
TEST(Func, test) {
ASSERT_TRUE(70 < (rand() % 100));
}
S——Self-verifying:自我验证
单元测试需要采用Asset函数等进行自验证,即当单元测试执行完毕之后就可得知测试结果,全程无需人工接入。
T——Timely:及时
等代码稳定运行再来补齐单元测试可能是低效的,最有效的方式是在写好功能函数接口后(实现函数功能前)进行单元测试。