lsekfe 发表于 2021-6-4 09:54:49

基于GTest进行单元测试的详细过程

1、快速了解GTest
  GTest全称GoogleTest,是Google公司发布的一款非常优秀的开源C/C++单元测试框架,已被应用于多个开源项目及Google内部项目中,包括ChromeWeb浏览器、LLVM编译器框架等。
GTest源码:https://github.com/google/googletest下载或克隆源码后,可以看见目录结构如下图,通常我们进行单元测试时需要用到目录是include和src。配置工程需要做以下三件事:
  包含目录:\googletest\include;\googletest;
  添加源文件:\googletest\src\gtest-all.cc;
  包含头文件:#include<gtest/gtest.h>
http://www.51testing.com/attachments/2021/05/15326880_2021052616324216yWs.jpg
  进行单元测试前我们需要了解两个概念:测试用户、测试用例集。
  测试用例:为了验证代码的行为与预期是否相符而进行的一系列活动,在单元测试中,这一系列的活动依靠代码来完成。
  测试用例集:多个相似或相关的测试用例的集合,是为了方便我们对测试用例进行管理而产生的一个概念。通俗一点,测试用例集就是对测试用例进行分组。
TEST(IsLeapYearTest,leapYear)/*用例集IsLeapYearTest,用例leapYear*/
  {
  EXPECT_TRUE(IsLeapYear(2000));/*测试IsLeapYear函数,传入参数2000*/
  EXPECT_TRUE(IsLeapYear(1996));
  } 写好测试用例后,需要运行测试用例,代码如下:
intmain(intargc,char**argv)
  {
  testing::FLAGS_gtest_filter=“*”;/*选择需要运行的用例*/
  testing::InitGoogleMock(&argc,argv);/*初始化测试框架*/
  returnRUN_ALL_TESTS();/*运行所选测试用例*/
  }写好测试用例后,GTest中可以用一下方式表示测试用例:
  “用例集.用例”,例如:“IsLeapYearTest.leapYear”
  可以使用通配符“*”和“”,例如:“IsLeapYearTest.*”
  使用“:”连接多个匹配条件,例如:“*.leapYear:*.commonYear”
  使用“-”排除用例,例如:“-IsLeapYearTest.*”
  2、断言
  断言可以理解为判断一个值或多个值是否满足指定条件,例如:
http://www.51testing.com/attachments/2021/05/15326880_202105261633031OEuS.png
更多GTest中断言的宏请查阅文档:\googletest\docs\primer.md
当判定通过时,无输出,如下图:
http://www.51testing.com/attachments/2021/05/15326880_202105261633231MmAJ.jpg
  当判定失败时,GTest会输出断言位置和失败原因,如下图:
http://www.51testing.com/attachments/2021/05/15326880_202105261633431Bi6X.jpg
  GTest中断言的宏可以理解为两类:ASSERT、EXPECT。
  ASSERT_*:当检查点失败时,退出当前函数(执行return操作)。
  EXPECT_*:当检查点失败时,继续往下执行。
  3、使用GTest说明FIRST原则
  看完以上内容,应该对GTest有了简单的认识,接下来就使用GTest框架来举例说明FIRST原则。
  (1)F-FAST(快速原则)
  在调试程序的过程中,需要多次运行单元测试去验证被测试模块是否正确,应该为了节省时间,单元测试必须可以快速执行。
 TEST(Fs,basic){/*测试文件接口的基本功能*/
  ASSERT_EQ(fs_test(os_fs()),RET_OK);/*测试获取文件系统对象函数os_fs()*/
  }(2)I-Independent(独立原则)
  单元测试可独立运行,测试用例直接无依赖,对外部资源无依赖,测试顺序不影响测试结果,测试过程中产生的外部资源文件需要在测试完成后销毁。
TEST(Fs,read_part){/*测试文件接口的读取功能*/
  charbuff;
  uint32_tsize=0;
  constchar*str="helloworld";
  constchar*filename="test.bin";
  /*测试前要创建被读取的文件*/
  file_write(filename,str,strlen(str));
  char*ret=(char*)file_read(filename,&size);
  /*进行测试,对读取到的结果ret进行验证*/
  ASSERT_EQ(file_read_part(filename,buff,sizeof(buff),0),strlen(str));
  ASSERT_EQ(strcmp(ret,str),0);
  ASSERT_EQ(size,strlen(str));
  /*测试完成后删除被读取的文件,并释放缓冲区*/
  file_remove(filename);
  TKMEM_FREE(ret);
  }(3)R-Repeatabl(可重复原则)
  单元测试需要可以稳定重复的运行,每次得到的结果需要保持一致,如果连测试结果都不稳定,或者测试过程经常出现失败的情况,那么单元测试也没有意义了。
 TEST(RandomNumber,Compared){/*测试随机数比较大小*/
  ASSERT_TRUE((rand()%100)>(rand()%100));/*无法保证每次结果都一致*/
  }(4)S-SelfValidating(自我验证原则)
  单元测试由用例自动进行验证的,不依赖人工验证,这是因为人工验证耗费不必要的时间,而且没有办法保证验证结果的准确性,通常来说单元测试的自我验证就是由测试程序直接告诉开发者通过或不通过,不需要让开发者通过输出结果来判断自己的测试用例是否通过。例如GTest的测试用例输出,输出OK表示通过,如下所示:
Fs.basic/*RUN表示执行Fs.basic测试用例*/
  Fs.basic(4ms)/*OK表示Fs.basic用例测试通过*/
  Fs.read_part
  Fs.read_part(0ms)(5)T-Timely(及时原则)
  单元测试必须及时进行编写,更新和维护,以保证用例可以随着业务代码的变化动态的保障质量。单元测试通常是在写函数实现前就需要写好的,这样能让单元测试在开发者写函数实现的过程中起到校验的作用,避免开发者犯错。
 TEST(Module,Function){/*测试某模块的功能(函数)*/
  ASSERT_TRUE(function());/*先按照期望结果测试function(),再去写function()的实现*/
  }








页: [1]
查看完整版本: 基于GTest进行单元测试的详细过程