[原创] something about Google Mock / gmock使用小结

本文基于gmock版本:1.5.0,本文内容不保证对后续版本一定正确。

下载gmock:http://code.google.com/p/googlemock/downloads/list

安装:将下载的压缩包解压出来,到解压目录下执行:

./configure

然后执行:

make

注意,这个make不会编译Google Mock自己的测试用例。要编译它们,需要到解压目录下的“make”子目录下,执行:

make gmock_test

文章来源:http://www.codelast.com/

然后就会看到该目录下生成了一个可执行文件 gmock_test ,执行这个可执行文件,就可以看到Google Mock测试用例的执行结果(应该是成功的,如果有失败,则应修改Makefile使之成功):

Running main() from gmock_main.cc

[==========] Running 13 tests from 3 test cases.

[----------] Global test environment set-up.

[----------] 6 tests from InitGoogleMockTest

[ RUN      ] InitGoogleMockTest.ParsesInvalidCommandLine

[       OK ] InitGoogleMockTest.ParsesInvalidCommandLine (0 ms)

[ RUN      ] InitGoogleMockTest.ParsesEmptyCommandLine

[       OK ] InitGoogleMockTest.ParsesEmptyCommandLine (0 ms)

[ RUN      ] InitGoogleMockTest.ParsesSingleFlag

[       OK ] InitGoogleMockTest.ParsesSingleFlag (0 ms)

[ RUN      ] InitGoogleMockTest.ParsesUnrecognizedFlag

[       OK ] InitGoogleMockTest.ParsesUnrecognizedFlag (0 ms)

[ RUN      ] InitGoogleMockTest.ParsesGoogleMockFlagAndUnrecognizedFlag

[       OK ] InitGoogleMockTest.ParsesGoogleMockFlagAndUnrecognizedFlag (0 ms)

[ RUN      ] InitGoogleMockTest.CallsInitGoogleTest

[       OK ] InitGoogleMockTest.CallsInitGoogleTest (0 ms)

[----------] 6 tests from InitGoogleMockTest (1 ms total)

[----------] 6 tests from WideInitGoogleMockTest

[ RUN      ] WideInitGoogleMockTest.ParsesInvalidCommandLine

[       OK ] WideInitGoogleMockTest.ParsesInvalidCommandLine (0 ms)

[ RUN      ] WideInitGoogleMockTest.ParsesEmptyCommandLine

[       OK ] WideInitGoogleMockTest.ParsesEmptyCommandLine (0 ms)

[ RUN      ] WideInitGoogleMockTest.ParsesSingleFlag

[       OK ] WideInitGoogleMockTest.ParsesSingleFlag (0 ms)

[ RUN      ] WideInitGoogleMockTest.ParsesUnrecognizedFlag

[       OK ] WideInitGoogleMockTest.ParsesUnrecognizedFlag (0 ms)

[ RUN      ] WideInitGoogleMockTest.ParsesGoogleMockFlagAndUnrecognizedFlag

[       OK ] WideInitGoogleMockTest.ParsesGoogleMockFlagAndUnrecognizedFlag (0 ms)

[ RUN      ] WideInitGoogleMockTest.CallsInitGoogleTest

[       OK ] WideInitGoogleMockTest.CallsInitGoogleTest (0 ms)

[----------] 6 tests from WideInitGoogleMockTest (0 ms total)

[----------] 1 test from FlagTest

[ RUN      ] FlagTest.IsAccessibleInCode

[       OK ] FlagTest.IsAccessibleInCode (0 ms)

[----------] 1 test from FlagTest (0 ms total)

[----------] Global test environment tear-down

[==========] 13 tests from 3 test cases ran. (1 ms total)

[  PASSED  ] 13 tests.

文章来源:http://www.codelast.com/

其他记录:

(1)Google Mock对象在析构的时候会检查mock函数的执行结果是否与预期相符,如果mock对象一直存在,最终的检查就不会发生。因此,如果你在堆(heap)中创建mock对象的话,最好使用内存泄漏检查工具来确定你的test是否做好了mock对象销毁工作。

(2)Google Mock要求在调用mock函数之前设置好预期的结果,否则其行为就是“未定义”的。

(3)使用EXPECT_CALL() 宏来设置一个mock函数调用的预期结果。其语法为:

EXPECT_CALL(mock_object, method(matchers))

.Times(cardinality)

.WillOnce(action)

.WillRepeatedly(action);

第一个参数是mock对象,第二个参数是mock函数名及其参数。二者中间是以逗号(而不是点号)分隔的,至于为什么是这样,回答只有一个:技术原因。

宏后面还可以紧跟若干语句,以提供对mock函数调用的预期结果的更多信息。这种风格的语法被一些人称作是“特定领域语言”(Domain-Specific Language,DSL)。

(4)Google Mock中有一些可能初次见到时感觉比较陌生的概念,例如 matcher,cardinality,etc.  这些概念在gmock的手册中都有详细说明,网上也可以找到一些中文文档,为了能真正用起来gmock,一定要看清了这些概念的含义。

(5)一个返回值为 bool 类型的mock函数默认是返回 false 的!所以,如果你没有设置函数的默认返回值,你的test函数被调用了之后很可能只执行了一部分(例如,你的test函数调用了一个mock函数,而该函数在返回 false 时,将不再继续执行其后的代码),这样你的test将不能覆盖某函数的全部代码,而这并不是你乐见的,切记。

(6)在mock对象析构时,对“期望”的检验才会被执行,所以,如果你的mock对象一直没有析构(例如,你在堆中创建了一个mock对象,却忘了删除它 。有人说可以用内存检查工具来防止这一点,但是要知道没有工具是可以保证100%可靠的),则检验一直不会被执行。如果你担心这种情况,就可以主动进行检验:

Mock::VerifyAndClearExpectations(&mock_object):

该方法返回一个bool值,以显示检验是否成功(true代表成功)。所以,你可以把它嵌套在EXPECT_TRUE()或者ASSERT_TRUE()里面来测试检验结果。

文章来源:http://www.codelast.com/

(7)gmock安装包解压目录下的“scripts”子目录下,有一个Python脚本fuse_gmock_files.py,这个脚本一般用不到,它是用来合并Google Mock的文件的。Google Mock包含N多文件,如果你要自己扩充它的功能,那么你就需要自行修改某些文件的源码,如果你把Google Mock拷贝到另一台计算机上,从头开始修改很多个文件的源码可能就很麻烦,所以脚本fuse_gmock_files.py就可以将Google Mock原来的N多文件合并为三个文件:gtest/gtest.h,gmock/gmock.h 和 gmock-gtest-all.cc。这样的话就方便多了。

(8)Google Mock有三个(主要的)指导文档,其难度由低到高分别为:ForDummies,CheatSheet,CookBook。所以,如果你刚接触gmock,应该按这个顺序依次学习那三个文档。

(9)对普通函数,使用宏 MOCK_METHODn 来定义一个模拟函数,其中n是你要模拟的函数的参数的个数,例如,你要模拟的函数没有参数,则应使用 MOCK_METHOD0。对const函数,你就要改用宏 MOCK_CONST_METHODn 来定义。

(10)static/静态 函数如何mock?

在Google Mock的官方“常见问题”的回答中(请点击这个链接,在文章中搜索“static”即可定位到),Google是这样说的:You can, but you need to make some changes(可以,但是你需要做一些修改).Google说,如果你需要mock一个静态函数,那说明你的程序模块过于“紧耦合”了(并且灵活性不够、重用性不够、可测试性不够),你最好是定义一个小接口,通过这个接口来调用那个函数,然后就容易mock了。

这就是Google Mock给出的答案,至于你怎么想的,又会不会按它的建议去做,决定权就在你自己了。

(11)使用 ON_CALL() 宏可以设定mocker函数的返回值,例如以下代码:

 

ON_CALL(myMockObj, myFunc())

    .WillByDefault(Return(123));

表示“命令”函数 myFunc() 的返回值为 123,其中 myMockObj 是mocker对象,函数 myFunc() 是在该mocker类中定义的模拟函数。可见,有了Goolge Mock这样的工具,我们就可以随意“控制”一个函数的行为,让它返回我们想要返回的值,从而可以使得单元测试可以按我们的意愿来进行——有时候,你必须要这么做,例如,你的一个函数 myInvoker() 调用了 myFunc() 函数,而myFunc() 函数的返回值是根据当前时间而变化的,因此其返回值不定,我们无法使其返回一个固定的值,从而我们在对 myInvoker() 做单元测试的时候,就“很难测”了,这个时候,我们就要命令 myFunc() 永远返回我们想要的值,从而可以使得对 myInvoker() 的单元测试可以顺利进行。

多么妙的想法!这就是mock的巨大作用,尽管它只是一个后台的“模仿者”(mocker),但是它产生的作用,却是真真切切的。

文章来源:http://www.codelast.com/

(12)编译你自己的单元测试的时候遇到“undefined reference to `vtable for XXX”,“undefined reference to `typeinfo for XXX'”错误?

出现这个错误,我可以提醒你的一点是:检查你的mocker类中,是否按照gmock文档的要求,把某些函数定义成纯虚函数了。比如我有一个tobe mocked的类:

class CTobeMocked

{

public:

    CTobeMocked() {}

    virtual ~CTobeMocked() {}

public:

    virtual double myFunc(const string &str) = 0;

};

这个类就是要被mock的类,注意里面的函数myFunc()是被定义成纯虚函数的,如果你漏了后面的“ = 0”,那么你编写了相应的mocker类之后,编译的时候就会出错。

我的mocker类如下:

class CMyMocker : public CTobeMocked

{

public:

    CMyMocker() {}

    virtual ~CMyMocker() {}

public:

    MOCK_METHOD(myFunc, double(const string &str));

};

这样就可以了。

文章来源:https://www.codelast.com/
➤➤ 版权声明 ➤➤ 
转载需注明出处:codelast.com 
感谢关注我的微信公众号(微信扫一扫):

wechat qrcode of codelast

发表评论