编译lib手动编译cmake编译gtest测试程序断言和caseFixture使用gmock编译gmock测试程序参考GtestGithub使用gtest(gmock)方便我们编写
编译 lib gtest 测试程序 使用 gmock 参考
Gtest Github 使用 gtest(gmock) 方便我们编写组织 c++ 单元测试。
编译 lib 到 github 拉取代码或者下载某个版本的 zip 包到本地目录,参考 gtest 中的 README.md 如何编译库和编译自己的代码,下面简单介绍下编译方法
手动编译 $ g++ -isystem ${GTEST_DIR} /include -I${GTEST_DIR} \-pthread -c ${GTEST_DIR} /src/gtest-all.cc $ ar -rv libgtest.a gtest-all.o
cmake 编译 gtest 已经提供了 cmakelist,可以直接使用cmake 生成 makefile, 编译库和 sample
$ mkdir mybuild $ cd mybuild $ cmake ${GTEST_DIR} $ make
然后就可以在编译自己的测试程序时链接 gtest 了。
$ g++ -isystem ${GTEST_DIR} /include -pthread path/to/your_test.cc libgtest.a -o your_test
跟多详细内容参考 readme 和代码中提供的例子(samples ; make 目录下),比如如何解决重复定义宏等问题。
gtest 测试程序 通过 编程参考 和 源码中 sample 目录下的示例
,我们可以很快上手 gtest。gtest 定义了宏供我们写断言语句,一个或者多个断言组成我们的测试用例 case,多个测试用例有时候需要共享一些通用对象,可以把这些用例放在同一个 fixture 中。
断言和 case gtest 断言提供两个版本 * ASSERT_*
版本断言,在同一个 case 中(测试函数)中,ASSERT_* 失败就会终止当前用例,开始其他 case ; * EXPECT_*
版本,当断言失败时,会报错,但是会继续执行剩余语句。
完整的 宏定义, 或见源码 include/gtest/gtest.h
使用哪种语句断言取决自己用例场景,如当前语句失败时后续语句没有继续执行意义,则可以直接使用 ASSERT 终止,否则使用 EXPECT 可以发现更多错误。
如果用例之间不需要什么公用资源,相互独立,可以使用如下方式定义每一个 case
TEST(套件名,用例名) { }
进入目录 sample 中, 以 sample1_unittest.cc 为例子
#include "sample1.h" #include "gtest/gtest.h" TEST(IsPrimeTest, Negative) {EXPECT_FALSE(IsPrime(-1 )) <<"这样子失败时打印自己的信息" ; EXPECT_FALSE(IsPrime(-2 )); EXPECT_FALSE(IsPrime(INT_MIN));}TEST(IsPrimeTest, Negative) {EXPECT_FALSE(IsPrime(-1 ));ASSERT_FALSE(IsPrime(-2 )); EXPECT_FALSE(IsPrime(INT_MIN));}
编译修改的测试代码&#xff0c;其中 libgtest.a
是 gtest 的库。
g&#43;&#43; -isystem ../include/ ./sample1.cc ./sample1_unittest.cc -pthread ../libgtest.a ../libgtest_main.a
链接 libgtest_main.a
是为了使用 src/gtest_main.cc
中定义 main 函数&#xff0c;执行所用测试用例&#xff0c;否者&#xff0c;也可以自己定义 main。
#include #include "gtest/gtest.h" int main(int argc, char **argv) {printf ("Running main() from gtest_main.cc\n" );testing::InitGoogleTest(&argc, argv);return RUN_ALL_TESTS(); }
编译后执行输出 bin 直接运行便运行所有用例&#xff0c;可以使用 -h
查看可选的执行参数&#xff0c;如--gtest_filter&#61;IsPrimeTest.Negative
指定执行 套件和 case ; --gtest_output&#61;xml[:DIRECTORY_PATH/|:FILE_PATH]
生成报告等。
Fixture 多个用例需要使用相同的数据&#xff0c;每次都在用例中准备显得很重复麻烦&#xff0c;这时候&#xff0c;可以使用 Fixture 来构建用例&#xff0c;使多个用例共用相同的数据对象配置。 使用 Fiture 第一部是定义一个继承自::testing::Test
的类&#xff0c;在类中定义初始化函数&#xff0c;清理函数和声明需要使用的对象。
class QueueTest : public ::testing::Test { protected : void SetUp() override {q1_.Enqueue(1 );q2_.Enqueue(2 );q2_.Enqueue(3 );}Queue<int > q0_;Queue<int > q1_;Queue<int > q2_; }; TEST_F(QueueTest, IsEmptyInitially) {EXPECT_EQ(q0_.size(), 0 ); }
以上我们定义了一个套件 QueueTest &#xff0c; 当我们执行该套件用例时&#xff0c; 1. gtest 构建 QueueTest 实例 qt1&#xff1b; 2. 调用 qt1.SetUp() 初始化 3. 执行一个用例 4. 调用 qt1.TearDown() 清理 5. 析构 qt1 对象 6. 回到1&#xff0c;执行下一个用例
从步骤可知&#xff0c;不同用例之间&#xff0c;数据实际都是独占的&#xff0c;不会相互影响 。
使用 fixture 编写用例后&#xff0c;同单独测试用例 TEST 一样&#xff0c;需要编写 main &#xff0c;然后编译连接&#xff0c;执行测试。
使用 gmock gmock 现在已经和入 gtest 的代码库, 1.8 和之后的版本直接在 gtest github 主页中获取&#xff0c;低版本仍然在原 github主页。
gmock 需要依赖 gtest 使用&#xff0c;在测试中&#xff0c;当我们测试的对象需要依赖其他模块、接口&#xff0c;但是往往受条件限制无法使用真实依赖的对象&#xff0c;通过 mock 对象来模拟我们需要依赖&#xff0c;以协助测试本模块&#xff0c;mock 对象具有和真实对象一样的接口&#xff0c;但是我们可以在运行时指定他的行为&#xff0c;如何被使用&#xff0c;使用多少次、参数&#xff0c;使用时返回什么等。
编译 编译说明 gmock 编译需要依赖 gtest&#xff0c; 准备好 gtest 和 gmock &#xff08;同一个版本&#xff09;后&#xff0c;手动编译的方法如下&#xff1a; 设置好 gtest 和 gmock 的工程路径&#xff0c;或者在下面命令中直接替换源路径。
g&#43;&#43; -isystem ${GTEST_DIR} /include -I${GTEST_DIR} \-isystem ${GMOCK_DIR} /include -I${GMOCK_DIR} \-pthread -c ${GTEST_DIR} /src/gtest-all.cc g&#43;&#43; -isystem ${GTEST_DIR} /include -I${GTEST_DIR} \-isystem ${GMOCK_DIR} /include -I${GMOCK_DIR} \-pthread -c ${GMOCK_DIR} /src/gmock-all.cc ar -rv libgmock.a gtest-all.o gmock-all.o
由命令可知&#xff0c;libgmock.a 包含了 libgtest.a &#xff0c;所有实际编译测试程序时&#xff0c;只需要链接 libglmock.a 就好了。
使用 cmake编译库&#xff0c;进入 gmock 目录&#xff08;此处 gtest 已经准备并且与 gmock 同级目录&#xff09;
$ cd ./googlemock/; mkdir build $ cd ./build; cmake .. $ make
生成 libgmock.a 库在 build 目录下&#xff0c; 同时生成 libgtest.a gtest/
下&#xff0c; 与上面手动编译把 gtest 和 gmock 打在一个 libgmock.a 不同&#xff0c;使用这种编译程序需要同时指定 链接 libgmock.a 和 libgtest.a &#xff0c; 否则会报各种 undefine 的错误 。
编译测试程序 &#xff1a;
g&#43;&#43; -isystem ${GTEST_DIR} /include \-isystem ${GMOCK_DIR} /include \-pthread path/to/your_test.cc libgmock.a -o your_test
测试时&#xff0c;我链接 cmake 编译出来的库时报错&#xff0c;查看库中很多符号没有&#xff0c;原因就是 cmake 输出的 libmock.a 不包含 gtest&#xff0c;需要指定链接 libgtest.a
gmock 测试程序 参考 gmock 编程指导和 codebook
gmock mock 对象&#xff0c;可以定义函数期望行为&#xff0c;如被调用时返回的值&#xff0c;期望被调用的次数&#xff0c;参数等&#xff0c;如果不满足就会报错。 定义 gmock 对象的基本步骤&#xff1a; 1. 创建 mock 对象继承自原对象&#xff0c;并用框架提供的宏 MOCK_METHODn(); (or MOCK_CONST_METHODn();
描述需要模拟的接口 2. 写用例&#xff0c;在用例中使用宏定义期望接口的行为&#xff0c;如果定义的行为执行用例时不满足&#xff0c;就会报错
借用主页提供的例子改写&#xff0c;简单学习下如何使用 mock
比如你测试的对象依赖的接口定义如下&#xff0c;
class Turtle {public :virtual ~Turtle() {}virtual void PenUp() &#61; 0 ;virtual void PenDown() &#61; 0 ;virtual void Forward(int distance) &#61; 0 ;virtual void Turn(int degrees) &#61; 0 ;virtual void GoTo(int x, int y) &#61; 0 ;virtual int GetX() const &#61; 0 ;virtual int GetY() const &#61; 0 ;};
此时通过继承这个对象&#xff0c;定义了 mock 对象&#xff0c;在对象中通过宏描述需要 mock 的接口&#xff0c;这样&#xff0c;就完成了对象的 mock 操作。
#include "gmock/gmock.h" #include "gtest/gtest.h class MockTurtle: public Turtle {public :MOCK_METHOD0(PenUp, void ());MOCK_METHOD0(PenDown, void ());MOCK_METHOD1(Forward, void (int distance));MOCK_METHOD1(Turn, void (int degrees));MOCK_METHOD2(GoTo, void (int x, int y));MOCK_CONST_METHOD0(GetX, int ());MOCK_CONST_METHOD0(GetY, int ());};
定义了 mock 对象后&#xff0c;就可以在测试用例使用 mock 对象替代原依赖对象&#xff0c;执行测试了。
using ::testing::AtLeast;TEST(PainterTest, PenDownCall) {MockTurtle turtle;EXPECT_CALL(turtle, PenDown())┊ .Times(AtLeast(2 ));turtle.PenDown();turtle.PenDown();}using ::testing::Return;TEST(PainterTest, GetX) {MockTurtle turtle;EXPECT_CALL(turtle, GetX())┊ .Times(4 )┊ .WillOnce(Return(100 ))┊ .WillOnce(Return(150 ))┊ .WillRepeatedly(Return(200 ));EXPECT_EQ(turtle.GetX(), 100 );EXPECT_EQ(turtle.GetX(), 150 );EXPECT_EQ(turtle.GetX(), 200 );EXPECT_EQ(turtle.GetX(), 200 );}using ::testing::_;TEST(PainterTest, GoTo) {MockTurtle turtle;EXPECT_CALL(turtle, GoTo(_, 100 ));turtle.GoTo(1 , 100 );EXPECT_CALL(turtle, GoTo(_, 101 ));turtle.GoTo(2 , 101 );}
gmock 使用宏设置期望是粘性的&#xff0c;意思是当我们调用达到期望后&#xff0c;这些设置的期望仍然保持活性。 举个例子&#xff0c;mock 一个接口 a(int)&#xff0c;我们设置第一个期望&#xff1a; a 调用传入参数任意&#xff0c;调用次数任意&#xff1b;然后设置第二个期望&#xff1a; a 调用传入参数必须为1&#xff0c; 调用次数为2&#xff1b;当我们调用 a(1) 两次后&#xff0c;达到了第二个期望上边界&#xff08;此时第二个期望并不会失效&#xff09;&#xff0c;这时候&#xff0c;第三次调用 a(1) 就会报错&#xff0c;因为匹配到第二个期望说调用超过2次。&#xff08;总是匹配最后一个期望 &#xff09; 如果想设置多个期望&#xff0c;并按顺序执行&#xff0c;可以如下实现
TEST(PainterTest, GetY) {using ::testing::InSequence;InSequence dummyObj;MockTurtle turtle;EXPECT_CALL(turtle, GetY())┊ .Times(2 )┊ .WillOnce(Return(100 ))┊ .WillOnce(Return(150 ))┊ .RetiresOnSaturation(); EXPECT_CALL(turtle, GetY())┊ .Times(1 )┊ .WillOnce(Return(200 ))┊ .RetiresOnSaturation();EXPECT_EQ(turtle.GetY(), 100 );EXPECT_EQ(turtle.GetY(), 150 );EXPECT_EQ(turtle.GetY(), 200 );}
最后&#xff0c;和 gtest 中一样&#xff0c;可以自己编写 main 函数完成调用&#xff0c;不过注意到&#xff0c;调用的 init 函数不同&#xff0c;之后便可以按前面提到的编译命令执行编译&#xff0c;运行测试了。
int main(int argc, char ** argv) {::testing::InitGoogleMock(&argc, argv);return RUN_ALL_TESTS();}
参考 Advanced googletest Topics gmock 使用参考 gmock defind