Linux项目搭建Gtest/Gmock框架总结

您所在的位置:网站首页 gtest官方文档 Linux项目搭建Gtest/Gmock框架总结

Linux项目搭建Gtest/Gmock框架总结

2024-03-04 02:03| 来源: 网络整理| 查看: 265

1. 前言

近期组里项目代码出于后期测试和长期维护的考虑,需要集成测试框架和用例补全,现有开源测试框架里面,google的test框架对于C++项目的支持度较高,且官方文档的完善性也高,便于上手,所以选用gtest这一套。

2. gtest环境搭建 下载地址:

https://github.com/google/googletest/

环境搭建:

Git下来的源码里面包含了googletest和googlemock的文件夹,如果是做简单的gtest学习可以直接cmake编译就可以直接使用。如果需要加入到自己的C++项目中,需要编写Cmake文件,生成gtest/gmock静态库,再将编写的测试用例链接库就可以使用。我当前使用的目录结构如下图: 工程根目录 其中src为项目的源码文件夹,test文件夹用于存放测试用例,thirdpart用于存放三方库(gtest源码放在这里面)用于编译链接使用。 从github上下载的源码包含很多可选文件及文件夹,我本人使用的gtest文件目录主要包含这些文件夹,省去了没有用到的部分 在这里插入图片描述 在这里插入图片描述 剩下的就是Cmake文件的编写,写了个简单的demo如下:

project(myproject) cmake_minimum_required(VERSION 3.0) # 配置gcc编译参数 set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}") set(CMAKE_CXX_FLAGS "-O0 -m64 -g -fPIC ${CMAKE_CXX_FLAGS}") set(GOOGLETEST_VERSION 1.10.0) set(LIBRARY_OUTPUT_PATH lib) #定义一些编译宏 add_definitions(-DFOR_GTEST) #编译选项开关 option(BUILD_GMOCK "Builds the googlemock subproject" ON) option(INSTALL_GTEST "Enable installation of googletest. (Projects embedding googletest may want to turn this OFF.)" ON) # 头文件包含的目录 include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/thirdpart/gtest/googlemock/include ${CMAKE_CURRENT_SOURCE_DIR}/thirdpart/gtest/googlemock/src ${CMAKE_CURRENT_SOURCE_DIR}/thirdpart/gtest/googletest/include ${CMAKE_CURRENT_SOURCE_DIR}/thirdpart/gtest/googletest/src ) # 需要包含的编译子目录 if(BUILD_GMOCK) add_subdirectory( thirdpart/gtest/googlemock ) else() add_subdirectory( thirdpart/gtest/googletest ) endif() add_subdirectory( thirdpart/csv-parse) # 编译单元测试用例代码 add_executable(unittest test/main/gmock_main.cc test/unit_test/xxx-test.cc ) target_link_libraries(unittest -pthread gtest gmock ) gtest运行参数 命令行参数:–gtest_output=“xml:report.xml”,可以把控制台内容输出转储为 XML 格式,其中report为存储的文件名,e.g. ./unittest --gtest_output="xml:report.xml" 命令行参数:–gtest_repeat=n,表示进行n次的重复测试,某些测试进行多次尝试后可能会结果不一致,可以使用重复测试功能进行测试;参数–gtest_break_on_failure可以支持自动调试,运行测试有时候会失败,但是在大多数时候会顺利通过。这是与内存损坏相关的问题的典型特点。如果多次运行测试,就能够提高发现失败的可能性,e.g. ./unittest --gtest_repeat=2 --gtest_break_on_failure

表示重复运行相同的测试用例两次,如果测试失败,会自动调用调试器。

命令行参数:运用–gtest_filter来过滤case。有时候我们并不需要运行所有测试,尤其是在修改的代码只影响某几个模块的情况下。为了支持运行一部分测试,Google 提供 --gtest_filter=,e.g. ./unittest --gtest_filter=* #执行所有测试 ./unittest --gtest_filter=PoissonUdpClientTest* #执行PoissonUdpClientTest开头的测试 ./unittest --gtest_filter=-PoissonUdpClientTest* #不执行PoissonUdpClientTest开头的测试 测试用例参数:利用DISABLED 关键词实现临时禁用测试 说明:只需在逻辑测试名或单元测试名前面加上 DISABLE_ 前缀,它就不会执行了,e.g. TEST(DISABLE_FooTest, DoSomething) 命令行参数:–gtest_also_run_disabled_tests,对于DISABLE的用例依然执行,e.g. ./unittest –gtest_also_run_disabled_tests 3. 编写测试用例 TEST使用

常规的简单测试可以使用TEST宏,具体形式如下:

// TestName表示本次测试的名称,第二个参数TestDecription用作描述,执行时控制台显示 TestName.TestDescription可以看到用例执行结果 TEST(TestName, TestDescription) { … }

宏展开后的实质内容为TEST测试类:

class TestName_ TestDescription_Test : public testing::Test { ... void TestBody() { … } ... }; 单元测试套件和TEST_F的使用

在单元测试中,我们经常需要在某个测试套件、测试用例或者整个测试运行之前进行前置条件设置及检查,或者运行之后对运行结果进行校验等操作,包括初始化操作和资源回收等,谷歌提供了这样一套测试套件,下面举例说明。

class FooTest : public ::testing::Test { protected: //Override this to define how to set up the environment. virtual void SetUp() {} // Override this to define how to tear down the environment. virtual void TearDown() {} // shared resource initialize static void SetUpTestCase() {} // shared resource delete static void TearDownTestCase() {} };

测试套件类经常需要使用到的接口主要是以上4个,需要继承::testing::Test类,重写SetUp()和TearDown()两个虚方法,在每个测试套件用例执行前会执行SetUp()的部分,在每个测试套件用例执行完之后会执行TearDown()的部分,主要用于每个执行用例的前置初始化和环境清除。 在测试套件的第一个测试用例开始前,SetUpTestCase函数会被调用,而在测试套件中的最后一个测试用例运行结束后,TearDownTestCase函数会被调用。适用场景主要是: 1)初始化数据涉及内存申请等操作,为每个测试实例构造对象将带来较大系统开销; 2)存在某数据,其在每个实例中均被用到,但每个实例都不会更改该数据的值。

有了上面的测试套件,接下来就只需写TEST_F测试用例了,形式参考

// 第一个参数必须为测试套件类名,第二个参数为测试描述 TEST_F(FooTest, FuncA) { Foo foo; foo.FuncA(); } 值参数化和TEST_P使用

在测试用例中,我们时常需要传给被测函数不同的值,gtest为我们提供了简便的方法,可以使我们能够灵活的进行参数化测试。 这里直接给出最通用化场景值参数化的使用步骤:

将需要使用的多参数封装成一个参数类,如 //Step1:申明一个呼叫参数类,该类主要用于TEST_P宏中实现的测试逻辑使用 // 把需要用到两个参数封装成一个参数类 class CallArgs { public: CallArgs(int argOne, double argTwo) : m_argOne(argOne), m_argTwo(argTwo) {} int GetArgOne() { return m_argOne; } double GetArgTwo() { return m_argTwo; } private: int m_argOne; double m_argTwo; }; 申明一个呼叫类,该类类名同时也是TEST_P宏的第一个参数test_case_name // 该类继承了TestWithParam模版类,从而使得CallArgs类与Call类进行了关联。 class Call: public ::testing::TestWithParam { }; 使用INSTANTIATE_TEST_CASE_P宏,对Call类进行类相关多个的参数设置 // 这里只添加了两组参数,事实上,可以添加更多。 // 这里使用参数生成器::testing::Values,GTest定义了了很多参数生成器。 INSTANTIATE_TEST_CASE_P(VOIP, Call, ::testing::Values( CallArgs(2, 9.6), CallArgs(4, 14.5) ) ); 编写了使用TEST_P宏实现的测试用例 // 使用了TestWithParam类的GetParam()接口获取参数CallArgs // 实际上这是两个测试用例,即该代码段会执行两组用例 TEST_P( Call, makeCall) { CallArgs args = GetParam(); ASSERT_TRUE(makeCall(args.GetArgOne(), args.GetArgTwo())); } protected/private方法测试

测试类的public方法,测试配置的输入一般为入参和成员变量的状态值,测试的输出一般为返回值和某些成员状态变化的值,但是类对象不能直接访问protected/private成员。 如果需要测试类的protected/private方法,我这里总结了3种方法:

首先一般不建议对非public成员函数直接访问,如果需要测试非public成员函数,可以被测试的类中新建一些public接口进行调用的方式;

可以在被测类的头文件中加入类似下面这种宏定义,将protected/private成员转为public访问类型

#ifdef FOR_GTEST #define private public #define protected public #endif

然后在cmake文件中加入FOR_GTEST的编译宏,在测试代码中就可以直接访问调用

add_definitions(-DFOR_GTEST) 使用gtest框架的提供的FRIEND_TEST 在被测类的头文件需要引入包含该宏的头文件gtest/gtest_prod.h,然后添加宏进行友元声明,简单示例如下: // foo.h #include // Defines FRIEND_TEST. class Foo { ... private: FRIEND_TEST(FooTest, BarReturnsZeroOnNull); int Bar(void* x); }; // foo_test.cc ... TEST(FooTest, BarReturnsZeroOnNull) { Foo foo; EXPECT_EQ(0, foo.Bar(NULL)); // Uses Foo's private member Bar(). } gmock使用

gmock是谷歌推出的开源白盒测试工具,用于编写C++模拟类的框架。通过gmock可以用一些简单的宏描述想要模拟的接口并指定其期望,在测试中有效地去除外部依赖,更方便地测试模块功能。 对类里面需要打桩的函数mock,语法如下:

MOCK_METHODn(..., ...); //其中n表示参数的个数 MOCK_CONST_METHODn(..., ...); //const成员方法用这种

对mock的方法可以指定期望,包括返回值,调用次数等,使用EXPECT_CALL()宏:

EXPECT_CALL(mock_object, method(matchers)) .Times(cardinality) //可以指定调用几次 .WillOnce(action) //可以指定调用行为 .WillRepeatedly(action);

Matchers指参数匹配器,可以指定任意参数,::testing::_ 表示输入的参数为任意参数,其他参数不一一列举

Mock virtual方法如下,不需要对工程代码做修改:

// Foo.h class Foo { public: virtual int FooFuncOne(int num); }; // FooTest.cc class MockFoo : public Foo { public: MOCK_METHOD1(FooFuncOne, int(int num)); }; TEST(FooTest, FooFuncOne) { MockFoo mockFoo; // 指定FooFuncOne返回值为5 EXPECT_CALL(mockFoo, FooFuncOne((::testing::_))) .WillRepeatedly((::testing::Return)(5)); }

上面mock方法主要用到了虚函数重写,但是对于非虚函数,上述写法是不能够生效的,需要有较大的改动,可以根据实际需要选择。下面贴了使用的示例:

// foo.h class Foo { public: void CallSelfMethod(); void PublicMethod(); protected: void ProtectedMethod(); private: void PrivateMethod(); }; // 重构成模板类 foo_testable.h template class FooTestable { public: FooTestable(T &self); void CallSelfMethod(); void PublicMethod(); protected: void ProtectedMethod(); private: void PrivateMethod(); T &self; }; // foo_test.cc class MockFoo { public: MOCK_METHOD(PublicMethod, void()); MOCK_METHOD(ProtectedMethod, void()); MOCK_METHOD(PrivateMethod, void()); }; TEST(Test_MockSelfNonVirtualMethod, SelfMethod) { MockFoo mockFoo; FooTestable fooTestable(mockFoo); EXPECT_CALL(mockFoo, PublicMethod()).Times(1); EXPECT_CALL(mockFoo, ProtectedMethod()).Times(1); EXPECT_CALL(mockFoo, PrivateMethod()).Times(1); fooTestable.CallSelfMethod(); } 4. 常用断言宏

在这里插入图片描述

5. 参考资料 gmock官方入门/进阶文档 googlemock/docs/ForDummies.md googlemock/docs/CookBook.md博客 mock非虚函数


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3