在上一讲“单元测试的尝试”里我们遇到了几个问题:
1、代码重复的问题太多
2、测试结果需要人工去检查
3、对测试的总体信息也无从得知
本讲将构建一个简单的单元测试框架来解决以上的问题:
1、代码重复的问题太多。
这个问题很容易解决,只需要把判断预期结果和实际结果的逻辑提取到某个函数中即可。从整个代码来看,有两种类型的结果的函数:
(1)返回布尔型
(2)返回整数
因此,需要两个类型的判断预期结果和实际结果是否相符的函数:XUnit系列的框架的习惯使用assert*的命名来定义判断函数,对于通过的测试习惯打印一个“.”号,而对于失败的测试习惯打印一个“F”。
- /*
- * 判断是否取值为真
- */
- void assertTrue(char *msg, bool actual)
- {
- if(actual)
- {
- printf(".");
- }
- else
- {
- printf("F");
- }
- }
- /*
- * 判断预期结果和实际结果是否相符
- */
- void assertEquals(char *msg, int expect, int actual)
- {
- if(expect == actual)
- {
- printf(".");
- }
- else
- {
- printf("F");
- }
- }
小知识:
2、测试结果需要人工去检查
对于测试结果不要使用printf方式打印被测试函数的返回结果值就可以避免这个问题。
3、对测试的总体信息也无从得知
除了问题1的解决办法里使用“.”表示测试通过和“F”表示测试失败可以提高对测试结果的信息的直观性之外,做单元测试的人还希望能够得到以下的信息:
(1)执行的测试用例总数、通过的数量和失败的数量
(2)测试执行的时间
(3)如果测试用例执行失败了,希望知道是哪个测试用例失败,从而去分析失败的原因。
下面来实现以上三个目标:
1、 为了获取已执行的测试用例数量、通过的数量和失败的数量,以及测试执行的时间,需要定义一些变量来保存这些信息:
- int errorCount = 0;
- int testCount=0;
- time_t startTime, endTime;
2、为了获取失败的测试用例信息,需要先定义一个变量来保存错误信息,同时,上面的两个断言函数也需要增加一个参数,用来传递当失败的时候要显示的失败信息。为了方便记录错误的信息,可以添加两个处理错误信息的函数:
- /*
- * 存放测试信息和错误信息的全局变量
- */
- char *errors[100] = {""};
- int errorCount = 0;
- int testCount=0;
- time_t startTime, endTime;
- /*
- * 判断是否取值为真
- */
- void assertTrue(char *msg, bool actual)
- {
- testCount++;
- if(actual)
- {
- printf(".");
- }
- else
- {
- printf("F");
- addError(msg);
- }
- }
- /*
- * 判断预期结果和实际结果是否相符
- */
- void assertEquals(char *msg, int expect, int actual)
- {
- testCount++;
- if(expect == actual)
- {
- printf(".");
- }
- else
- {
- printf("F");
- addError(msg, expect, actual);
- }
- }
失败信息的处理函数:
- /*
- * 添加错误信息
- */
- void addError(char *msg)
- {
- char error[100] = "Test '";
- strcat(error, msg);
- strcat(error, "' is failed!");
- errors[errorCount] = new char[100];
- strcpy(errors[errorCount], error);
- errorCount ++;
- }
- /*
- * 添加错误信息,带预期结果与实际结果参数
- */
- void addError(char *msg, int expect, int actual)
- {
- char error[100] = "Test '";
- char num[10];
- strcat(error, msg);
- strcat(error, "' is failed!");
- strcat(error, " Expected: ");
- strcat(error, itoa(expect, num, 10));
- strcat(error, " , actual: ");
- strcat(error, itoa(actual, num, 10));
- errors[errorCount] = new char[100];
- strcpy(errors[errorCount], error);
- errorCount ++;
- }
3、计算测试所花费的时间:
在测试开始之前开始计时,测试结束之后,停止计时。
- /*
- * 初始化测试,开始计时
- */
- void init()
- {
- printf("\n****** Test start ******\n");
- startTime = clock();
- }
- /*
- * 结束测试,结束计时,打印报告
- */
- void end()
- {
- endTime = clock();
- }
4、测试结束之后,把收集到的信息打印出来:
- /*
- * 测试报告
- */
- void testReport()
- {
- printf("\n\nTotal run Tests:"); //测试概要信息
- printf("%d", testCount);
- printf(", passed:%d", testCount-errorCount);
- printf(", failed:%d\n", errorCount);
- printf("Test escaped time: %6.3f seconds\n", (double)(endTime-startTime)/1000.0);
- if(errorCount>0) //测试失败的详细信息
- {
- printf("\n**************** Failed Test's Detail ****************\n\n");
- for(int i=0; i<errorCount; i++)
- {
- printf(" %d: ", i+1);
- printf(errors[i]);
- printf("\n");
- }
- printf("\n**************** End of Failed Detail ****************\n\n");
- }
- else //所有测试都通过
- {
- printf("\n****** All Tests had Passed! ******\n\n");
- }
- }
把这个函数放在end函数中调用:
- /*
- * 结束测试,结束计时,打印报告
- */
- void end()
- {
- endTime = clock();
- testReport();
- }