我是靠谱客的博主 敏感书本,最近开发中收集的这篇文章主要介绍理解使用GMock,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

Mock的定义:

在单元测试、模块的接口测试时,当一个模块需要依赖另外一个或几个类,而这时所依赖的类还没有开发好,这时就可以定义Mock对象来模拟那些类的行为。也就是自己实现一个假的依赖类,对这个类的方法想要什么行为就可以有什么行为,想让这个方法返回什么结果就可以返回什么样的结果。(便捷的模拟对象的方法。)

gmock 依赖C++多态机制进行工作,只有虚函数才能被mock, 非虚函数不能被mock, 这一点告诉我们,如果想要在代码中使用gmock类的设计中,最好采用接口隔离,对于c++来说也就是采用纯虚类型,因为c++本身没有接口类型。

当一个文件中多个方法需要调用一个未完成或者做单元测试时,就需要将这个未完成或者使用比较麻烦的虚方法Mock,因为不希望为了执行单元测试,专门搭建一个服务器与XXXClient交互,成本太高。注意:只需要Mock这个被调用的虚方法,调用方法不需要Mock,如果没有返回值可以使用SetArgReferee<0>(),参数从0开始。

For example:

    MOCK_METHOD5(Send, void(const String&,const string&,std::vector<string>&,ncOSSResponse&,int));

    std::vector<ncFileVersionInfo> versions;
    ncFileVersionInfo info1;
    info1.versionId = _T("1");
    info1.fileName = _T("zgh.txt");
    info1.editorName = _T("zgh");
    ncFileVersionInfo info2 = info1;
    versions.push_back (info1);
    versions.push_back (info2);

    // 构造http内容
    JSON::Array jArray;
    for (size_t i = 0; i < versions.size (); ++i) {
        JSON::Object jsonO;
        jsonO.insert (make_pair (JSON_MOVE (string ("rev")), JSON_MOVE (JSON::Value (toSTLString (versions[i].versionId)))));
        jsonO.insert (make_pair (JSON_MOVE (string ("name")), JSON_MOVE (JSON::Value (toSTLString (versions[i].fileName)))));
        jsonO.insert (make_pair (JSON_MOVE (string ("editor")), JSON_MOVE (JSON::Value (toSTLString (versions[i].editorName)))));
        
        jArray.push_back (jsonO);
    }
    JSON::Object json;
    json.insert (make_pair (JSON_MOVE (string ("versions")), JSON_MOVE (jArray)));
    string content;
    content.reserve (64);
    JSON::Writer::write (json, content);
    ncOSSResponse test;
    test.body = content;
    EXPECT_CALL(*_ClientMock, Send(_, _, _, _, _))
        .WillOnce(SetArgReferee<3>(test));

GMock的特性:

google mock是用来配合google test对C++项目做单元测试的。它依赖于googletest

轻松创建mock类,支持丰富的匹配器和行为,支持有序、无序、部分有序的期望行为的定义,多平台的支持

 

使用流程:

引入你要用到的GMock名称。除宏或其他特别提到的之外所有GMock名称都位于testing命名空间之下,建立模拟对象(mock_object)

EXPECT_CALL(mock_object,method(matcher1,matcher2,...))

.With(multi_argument_matcher)

.Times(cardinality)

.InSequence(sequences)

.After(expectations)

.WillRepeatedly(action)

.RetiresOnSaturation();

EXPECT_CALL 声明一个调用期待,就是我们期待这个对象的这个方法按照什么样的逻辑去运行。

mock_object      mock对象

Method               mock对象中的mock方法,他的参数可以通过matchers规则去匹配

With                    多个参数的匹配方式指定

Times                 表示这个方法可以被多次调用

InSequence        指定函数执行的顺序,他是通过同一序列中声明期待的顺序确定的

After                    指定某个方法只能在另一个方法之后执行

WillOnce              表示执行一次方法时,将执行其参数action的方法,一般用Return方法,用于指定一次调用的输出

WillRepeatedly     表示一直调用一个方法时,将执行其参数action方法,需要注意他和WillOnce的区别,WillOnce时一次,WillRepeatedly时一直。

RetiresOnSaturation   用于保证期待调用不会被相同的函数的期待所覆盖

EXPECT_CALL(mock_object,method(_,_))  表示有两个参数

下划线(_),它是通配符,就是对任何输入参数都按之后要求执行

 

Google Mock的使用(例子):

class User

{

public:

        virtual   User() {}

        virtual  ~User() { }

public:

        virtual   bool  Login(const std::string& username,const std::string&  password) = 0; //登录

        virtual   bool  Pay(int money) = 0; //支付

        virtual  bool  Online() = 0; //是否登录

};

注意:析构函数必须虚函数,方法也的定义为纯虚函数

 

业务模块:用户登录,并且发起支付行为

class  Biz
{
public:
    void SetUser(User *user)
    {
         _user = user;
     }
    std::string  Pay(const std::string& username,const std::string& password,int money)
     {  
        std::string  ret;
         if(!_user)
         {   
             ret = "pointer is null.";
             return  ret;
          }
         if(!_user->Online())
         {      
            ret = "logout status.";  
            //尚未登录,要求登录  
             if(!_user->Login(username,password))
              {      
                 //登录失败      
                ret += "login error";    
                 return ret;
            }       
             else
              {           
                //登录成功 
                  ret += "login success.";
             }
          } 
        else
          {      
            //已登录        
            ret += "login status."; 
          }
         if(!_user->Pay(money))
         {             
            ret += "Pay error.";
         }  
        else    
        {               
            ret += "Pay success.";
         }  
            return ret;
         }
        private:         
            User*  _user;
};

这段逻辑的口语描述就是:我们先看看用户登录了没,如果没有登录则要求用户登录。如果登录失败,则直接返回;如果登录成功,则执行支付行为。最后将流程的状态输出。

class TestUser :public User

{

punlic:

  MOCK_METHOD2(Login,bool(const std::string&,const std::string&));

  MOCK_METHOD1(Pay,bool(int));

  MOCK_METHOD0(Online,bool());

};

 

MOCK_METHOD #1(#2,#3(#4))

#1是要Mock的方法有几个参数

#2是要Mock的方法名称

#3是要Mock的方法的返回值

#4是要Mock的方法的具体参数

 

TEST(TestUser , User)

{
    TestUser test_user;
    EXPECT_CALL(test_user, Online()).WillRepeatedly(Return(false));
    EXPECT_CALL(test_user, Login(StrNe("admin"), _)).WillRepeatedly(Return(true));
    EXPECT_CALL(test_user,Pay(_)).Times(5).WillOnce(Return(true)).WillOnce(Return(true)).WillRepeatedly(Return(false));

    Biz biz;
    biz.SetUser(&test_user);
    string user_ret = biz.pay("user", "", 1);
    cout << "test ret value: " << user_ret << endl;
    user_ret = biz.pay("user", "", 1);
    cout << "test ret value: " << user_ret << endl;
    user_ret = biz.pay("user", "", 1);
    cout << "test ret value: " << user_ret << endl;

}

第6行我们使用Times函数,它的参数5表示该函数期待被调用5次,从第6次的调用开始,返回默认值。Times函数后面跟着两个WillOnce,其行为都是返回true。这个可以解读为第一次和第二次调用Pay方法时,返回成功。最后的WillRepeatedly表示之后的对Pay的调用都返回false。
 

再来一个gmock的简单实例:

#include <gtet/gtest.h>
#include <gmock.gmock.h>
using namespace testing;


class A
{
public:    
    int set(int num)   
    { 
        value = num;
         return num; 
     }

   
     int get()   
    {    
         return value; 
      }  
     int value;
};


class MockA : public A
{
public:    
    MOCK_METHOD1(set,int(int num)); 
    MOCK_METHOD0(get,int());
};


TEST(Atest,getnum)
{
    MockA m_A;
    int a = 10;
    EXPECT_CALL(m_A,set(_))
             .WillRepeatedly(Return(a));
    int k = m.A.set(200));
    EXPECT_EQ(10,K);
}


int main(int argc,char *argv[])

    ::testing::InitGoogleTest(&argc,argv);
    return RUN_ALL_TESTS();
}

 

 

 

最后

以上就是敏感书本为你收集整理的理解使用GMock的全部内容,希望文章能够帮你解决理解使用GMock所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(57)

评论列表共有 0 条评论

立即
投稿
返回
顶部