使用Google C++ Testing Framework进行C++单元测试.docx
《使用Google C++ Testing Framework进行C++单元测试.docx》由会员分享,可在线阅读,更多相关《使用Google C++ Testing Framework进行C++单元测试.docx(15页珍藏版)》请在冰豆网上搜索。
![使用Google C++ Testing Framework进行C++单元测试.docx](https://file1.bdocx.com/fileroot1/2023-1/21/9059ece8-363a-4870-aef8-8b3c336107c3/9059ece8-363a-4870-aef8-8b3c336107c31.gif)
使用GoogleC++TestingFramework进行C++单元测试
使用GoogleC++TestingFramework进行C++单元测试
前几个月Google开源了它的测试框架,自称其旗下的上千个项目都在使用它。
今天我们就用它来尝尝鲜吧?
:
-)
安装:
下载GoogleC++TestingFramework,解压...
VC2005:
直接打开msvc\gtest.vcproj或msvc\gtest.sln,直接编译即可。
Linux/UnixGCC4.0:
传统过程:
./configure make
Mingw:
BCC:
用Mingw和BCB6编译需要修改一些代码,文章最后我会把修改过的文件放上来。
使用:
首先#include,当然工程的头文件路径要设置正确
1.简单测试TEST
假如我写了个函数,是计算阶乘的:
1.int Factorial( int n )
2.{
3. if(n==2) return 100; //故意出个错,嘻嘻
4. return n<=0?
1 :
n*Factorial(n - 1);
5.}
6.
7./******************************************************
8.*TEST:
定义一次测试
9.*第一个参数是测试用例名,第二个参数是测试名
10.*随后的测试结果将以"测试用例名.测试名"的形式给出
11.*******************************************************/
12.TEST(TestFactorial, ZeroInput)
13.{
14. //EXPECT_EQ稍候再说,现在只要知道它是测试两个数据是否相等的就行了。
15. EXPECT_EQ(1, Factorial(0));
16.}
17.
18.TEST(TestFactorial, OtherInput)
19.{
20. EXPECT_EQ(1, Factorial
(1));
21. EXPECT_EQ(2, Factorial
(2));
22. EXPECT_EQ(6, Factorial(3));
23. EXPECT_EQ(40320, Factorial(8));
24.}
25.
26.int main(int argc, TCHAR* argv[])
27.{
28. //用来处理Test相关的命令行开关,如果不关注也可不加
29. testing:
:
InitGoogleTest(&argc,argv);
30.
31. //看函数名就知道干啥了
32. int r = RUN_ALL_TESTS();
33.
34. //只是让它暂停而已,不然一闪就没了
35. std:
:
cin.get();
36.
37. //如果全部通过则返回0,否则返回1
38. return r;
39.}
40.//---------------------------------------------------------------------------
intFactorial(intn)
{
if(n==2)return100;//故意出个错,嘻嘻
returnn<=0?
1:
n*Factorial(n-1);
}
/******************************************************
*TEST:
定义一次测试
*第一个参数是测试用例名,第二个参数是测试名
*随后的测试结果将以"测试用例名.测试名"的形式给出
*******************************************************/
TEST(TestFactorial,ZeroInput)
{
//EXPECT_EQ稍候再说,现在只要知道它是测试两个数据是否相等的就行了。
EXPECT_EQ(1,Factorial(0));
}
TEST(TestFactorial,OtherInput)
{
EXPECT_EQ(1,Factorial
(1));
EXPECT_EQ(2,Factorial
(2));
EXPECT_EQ(6,Factorial(3));
EXPECT_EQ(40320,Factorial(8));
}
intmain(intargc,TCHAR*argv[])
{
//用来处理Test相关的命令行开关,如果不关注也可不加
testing:
:
InitGoogleTest(&argc,argv);
//看函数名就知道干啥了
intr=RUN_ALL_TESTS();
//只是让它暂停而已,不然一闪就没了
std:
:
cin.get();
//如果全部通过则返回0,否则返回1
returnr;
}
//---------------------------------------------------------------------------
运行结果:
测试框架指出:
TestFactorial.ZeroInput运行OK,运行OtherInput时出现三次结果和预期不符。
2.多个测试场景需要相同数据配置的情况,用TEST_F
1.//用TEST_F做同配置的系列测试
2.typedef std:
:
basic_string tstring;
3.struct FooTest :
testing:
:
Test {
4. //这里定义要测试的东东
5. tstring strExe;
6. //可以利用构造、析构来初始化一些参数
7. FooTest() {}
8. virtual ~FooTest() {}
9.
10. //如果构造、析构还不能满足你,还有下面两个虚拟函数
11. virtual void SetUp() {
12. // 在构造后调用
13. strExe.resize(MAX_PATH);
14. GetModuleFileName(NULL, &strExe[0], MAX_PATH);
15. }
16.
17. virtual void TearDown() { } // 在析构前调用
18.};
19.
20.//偶写的从完整路径里取出文件名的函数,待测试(路径分隔符假定为'\\')
21.tstring getfilename(const tstring &full)
22.{
23. return full.substr(full.rfind(_T('\\')));
24.}
25.
26.//偶写的从完整路径里取出路径名的函数,待测试(Windows路径)
27.tstring getpath(const tstring &full)
28.{
29. return full.substr(0, full.rfind(_T('\\')));
30.}
31.
32.TEST_F(FooTest, Test_GFN)
33.{
34. //测试getfilename函数
35. EXPECT_STREQ(_T("Project1.exe"), getfilename(strExe).c_str());
36.}
37.
38.TEST_F(FooTest, Test_GP)
39.{
40. //测试getpath函数
41. EXPECT_STREQ(
42. _T("D:
\\Code\\libs\\google\\gtest-1.2.1\\BCC_SPC\\bcc\\ex"),
43. getpath(strExe).c_str());
44.}
45.
46.int main(int argc, TCHAR* argv[])
47.{
48. //主函数还是一样地
49. testing:
:
InitGoogleTest(&argc,argv);
50. int r = RUN_ALL_TESTS();
51. std:
:
cin.get();
52. return r;
53.}
//用TEST_F做同配置的系列测试
typedefstd:
:
basic_stringtstring;
structFooTest:
testing:
:
Test{
//这里定义要测试的东东
tstringstrExe;
//可以利用构造、析构来初始化一些参数
FooTest(){}
virtual~FooTest(){}
//如果构造、析构还不能满足你,还有下面两个虚拟函数
virtualvoidSetUp(){
//在构造后调用
strExe.resize(MAX_PATH);
GetModuleFileName(NULL,&strExe[0],MAX_PATH);
}
virtualvoidTearDown(){}//在析构前调用
};
//偶写的从完整路径里取出文件名的函数,待测试(路径分隔符假定为'\\')
tstringgetfilename(consttstring&full)
{
returnfull.substr(full.rfind(_T('\\')));
}
//偶写的从完整路径里取出路径名的函数,待测试(Windows路径)
tstringgetpath(consttstring&full)
{
returnfull.substr(0,full.rfind(_T('\\')));
}
TEST_F(FooTest,Test_GFN)
{
//测试getfilename函数
EXPECT_STREQ(_T("Project1.exe"),getfilename(strExe).c_str());
}
TEST_F(FooTest,Test_GP)
{
//测试getpath函数
EXPECT_STREQ(
_T("D:
\\Code\\libs\\google\\gtest-1.2.1\\BCC_SPC\\bcc\\ex"),
getpath(strExe).c_str());
}
intmain(intargc,TCHAR*argv[])
{
//主函数还是一样地
testing:
:
InitGoogleTest(&argc,argv);
intr=RUN_ALL_TESTS();
std:
:
cin.get();
returnr;
}
运行结果:
瞧,GoogleC++测试框架毫不客气地指出偶的getfilename返回的字符串比预期的多了一个'\\'
快速入门:
GoogleTest提供了两种断言形式,一种以ASSERT_开头,另一种以EXPECT_开头,它们的区别是ASSERT_*一旦失败立马退出,而EXPECT_*还能继续下去。
断言列表:
真假条件测试:
致命断言
非致命断言
验证条件
ASSERT_TRUE(condition);
EXPECT_TRUE(condition);
condition为真
ASSERT_FALSE(condition);
EXPECT_FALSE(condition);
condition为假
数据对比测试:
致命断言
非致命断言
验证条件
ASSERT_EQ(期望值,实际值);
EXPECT_EQ(期望值,实际值);
期望值==实际值
ASSERT_NE(val1,val2);
EXPECT_NE(val1,val2);
val1!
=val2
ASSERT_LT(val1,val2);
EXPECT_LT(val1,val2);
val1ASSERT_LE(val1,val2);
EXPECT_LE(val1,val2);
val1<=val2
ASSERT_GT(val1,val2);
EXPECT_GT(val1,val2);
val1>val2
ASSERT_GE(val1,val2);
EXPECT_GE(val1,val2);
val1>=val2
字符串(针对C形式的字符串,即char*或wchar_t*)对比测试:
致命断言
非致命断言
验证条件
ASSERT_STREQ(expected_str,actual_str);
EXPECT_STREQ(expected_str,actual_str);
两个C字符串有相同的内容
ASSERT_STRNE(str1,str2);
EXPECT_STRNE(str1,str2);
两个C字符串有不同的内容
ASSERT_STRCASEEQ(expected_str,actual_str);
EXPECT_STRCASEEQ(expected_str,actual_str);
两个C字符串有相同的内容,忽略大小写
ASSERT_STRCASENE(str1,str2);
EXPECT_STRCASENE(str1,str2);
两个C字符串有不同的内容,忽略大小写
TEST宏:
TEST宏的作用是创建一个简单测试,它定义了一个测试函数,在这个函数里可以使用任何C++代码并使用上面提供的断言来进行检查。
TEST的第一个参数是测试用例名,第二个参数是测试用例中某项测试的名称。
一个测试用例可以包含任意数量的独立测试。
这两个参数组成了一个测试的全称。
就前面的例子来说:
我们要测试这个函数:
intFactorial(intn);//返回n的阶乘
我们的测试用例是:
1.测试输入0的情况;2.测试输入其它数据的情况,于是就有了:
1.TEST(TestFactorial, ZeroInput) {
2. EXPECT_EQ(1, Factorial(0));
3.}
4.
5.TEST(TestFactorial, OtherInput) {
6. EXPECT_EQ(1, Factorial
(1));
7. EXPECT_EQ(2, Factorial
(2));
8. EXPECT_EQ(6, Factorial(3));
9. EXPECT_EQ(40320, Factorial(8));
10.}
TEST(TestFactorial,ZeroInput){
EXPECT_EQ(1,Factorial(0));
}
TEST(TestFactorial,OtherInput){
EXPECT_EQ(1,Factorial
(1));
EXPECT_EQ(2,Factorial
(2));
EXPECT_EQ(6,Factorial(3));
EXPECT_EQ(40320,Factorial(8));
}
GoogleTest根据测试用例来分组收集测试结果。
因此,逻辑相关的测试应该在同一测试用例中;换句话说,它们的TEST()的第一个参数应该是一样的。
在上面的例子中,我们有两个测试,ZeroInput和OtherInput,它们都属于同一个测试用例TestFactorial。
TEST_F宏:
TEST_F宏用于在多个测试中使用同样的数据配置,所以它又叫:
测试夹具(TestFixtures)
如果我们的多个测试要使用相同的数据(如前例中,我们的Test_GFN和Test_GP都使用程序自身的完整文件名来测试),就可以采用一个测试夹具。
要创建测试夹具,只需:
1.创建一个类继承自testing:
:
Test。
将其中的成员声明为protected:
或是public:
,因为我们想要从子类中存取夹具成员。
2.在该类中声明测试中所要使用到的数据。
3.如果需要,编写一个默认构造函数或者SetUp()函数来为每个测试准备对象。
4.如果需要,编写一个析构函数或者TearDown()函数来释放你在SetUp()函数中申请的资源。
5.如果需要,定义你的测试所需要共享的子程序。
当我们要使用夹具时,使用TEST_F()替换掉TEST(),它允许我们存取测试固件中的对象和子程序:
1.TEST_F(test_case_name, test_name) {
2.... test body ...
3.}
TEST_F(test_case_name,test_name){
...testbody...
}
与TEST()一样,第一个参数是测试用例的名称,但对TEST_F()来说,这个名称必须与测试夹具类的名称一样。
对于TEST_F()中定义的每个测试,GoogleTest将会:
1.创建一个全新的测试夹具
2.通过SetUp()初始化它,
3.运行测试
4.调用TearDown()来进行清理工作
5.删除测试夹具。
注意,同一测试用例中,不同的测试拥有不同的测试夹具。
GoogleTest不会对多个测试重用一个测试夹具,测试对测试夹具的改动并不会影响到其他测试。
调用测试
TEST()和TEST_F()向GoogleTest隐式注册它们的测试。
因此,与很多其他的C++测试框架不同,你不需要为了运行你定义的测试而将它们全部再列出来一次。
在定义好测试后,你可以通过RUN_ALL_TESTS()来运行它们,如果所有测试成功,该函数返回0,否则会返回1.注意RUN_ALL_TESTS()会运行你链接到的所有测试——它们可以来自不同的测试用例,甚至是来自不同的文件。
当被调用时,RUN_ALL_TESTS()宏会:
1.保存所有的GoogleTest标志。
2.为一个测试创建测试夹具对象。
3.调用SetUp()初始化它。
4.在固件对象上运行测试。
5.调用TearDown()清理夹具。
6.删除固件。
7.恢复所有GoogleTest标志的状态。
8.重复上诉步骤,直到所有测试完成。
此外,如果第二步时,测试夹具的构造函数产生一个致命错误,继续执行3至5部显然没有必要,所以它们会被跳过。
与之相似,如果第3部产生致命错误,第4部也会被跳过。
重要:
你不能忽略掉RUN_ALL_TESTS()的返回值,否则gcc会报一个编译错误。
这样设计的理由是自动化测试服务会根据测试退出返回码来决定一个测试是否通过,而不是根据其stdout/stderr输出;因此你的main()函数必须返回RUN_ALL_TESTS()的值。
而且,你应该只调用RUN_ALL_TESTS()一次。
多次调用该函数会与GoogleTest的一些高阶特性(如线程安全死亡测试thread-safedeathtests)冲突,因而是不被支持的。
编写main()函数
你可以从下面这个模板开始:
1.#include "this/package/foo.h"
2.#include
3.namespace {
4. // 用于测试Foo类的测试夹具
5. class FooTest :
public testing:
:
Test {
6. protected:
7. // 下面四个方法如果没有代码的话的可以不用定义
8. FooTest() {
9. // 这里可以为每个测试做一些设置工作
10. }
11. virtual ~FooTest() {
12. // 可以做一些不会抛出异常的清理工作
13. }
14. // 如果构造和析构还不能满足你的设置、清理工作
15. // 你还可以在下面两个函数里做这些
16. virtual void SetUp() {
17. // 这里的代码在构造之后(在每次测试之前)马上执行
18. }
19. virtual void TearDown() {
20. // 这里的代码在每次测试之后(在析构之前)马上执行
21. }
22. // 可以在这里定义所有要用到 Foo 的测试用例中所需要的对象.
23. };
24. // 测试做Abc操作的Foo:
:
Bar() 方法.
25. TEST_F(FooTest, MethodBarDoesAbc) {
26. const string input_filepath = "myinputfile.dat";
27. const string output_filepath = "myoutputfile.dat";
28. Foo f;
29. EXPECT_EQ(0, f.Bar(input_filepath, output_filepath));
30. }
31. // 测试 Foo 的 Xyz 操作.
32. TEST_F(FooTest, DoesXyz) {
33. // Exercises the Xyz feature of Foo.
34. }
35.} // namespace
36.int main(int argc, char **argv) {
37. testing:
:
InitGoogleTest(&argc, argv);
38. return RUN_ALL_TESTS();
39.}
#include"this/package/foo.h"
#include