• 白盒测试之gmock入门篇


    一、gmock是什么

    gmock是google公司推出的一款开源的白盒测试工具。gmock是个很强大的东西,测试一个模块的时候,可能涉及到和其他模块交互,可以将模块之间的接口mock起来,模拟交互过程。其作用就类似白盒测试中的打桩的概念。

    下面简单的说说打桩在白盒测试中的重要性:

    1、比如银行业务,需要测试业务模块。此时,不可能去操作真实的数据库,并且搭建新的数据库可能比较复杂或者耗时。那么就可以用gmock将数据库接口地方打桩,来模拟数据库操作。

    2、比如要测试A模块,必过A模块需要调用B模块的函数。如果B模块还没有实现,此时,就可以用gmock将B模块的某些接口打桩。这样就可以让A模块的测试继续进行下去。

    3、比如网关设备,在用gtest测试device模块的时候,必须有真实的设备才能让测试进行下去。如果用gmock模拟一套sdk接口,那么无需真实的设备也能让测试进行下去。(本条仅限公司内部阅读)

    二、gmock如何获取

    gmock是google的开源工具,google的官网上是可以下载源码的。不过公司内部员工可以去0.2上去取,在“研发质量部”的文件夹下。gmock的最新版本是1.7.0。下载下来的压缩包名称为“gmock-1.7.0.zip”。解压后,如下:

    仔细观察发现,压缩包里面有gtest的文件夹,该文件夹里面就是gtest的源码。因为大部分情况下,gmock是配合gtest来做白盒测试的。关于gtest的工程相关介绍请查看本博客其他文章。

    本文暂时讨论windows下的gmock编译,工程相关的文件在msvc的文件夹中。msvc文件夹下又分为2005和2010文件夹,分别是用VS2005和VS2010建立的工程。

    用VS2010打开工程,然后直接编译。编译通过后,会得到gmock.lib,gmock_maim.lib,gmock_test.ext三个文件。其中gmock.lib就是我们所需要的库文件,其他两个工程本文暂不做阐述。

    三、gmock的第一个demo

    我们暂且模拟银行转账系统。银行转账系统涉及到数据库两个操作,首先要通过银行卡ID找到账户信息(包括账户余额等),然后在通过计算,将转账后的金额写入到转账双方。这样就完成了简单的银行转账业务。

    下面我们来mock(模拟)数据库的查询账户和写入金额的两个方法,定义一个头文件AccountManager.h :

    // AccountManager.h 
     // the interface of external services which should be mocked 
     // 需要被mock的服务接口
     #pragma once 
    
     #include <string> 
    
     #include "Account.h" 
    
     class AccountManager 
     { 
     public: 
         //通过账户号,找到对应的用户
        virtual Account findAccountForUser(const std::string& userId) = 0; 
        //更新用户的信息这里
        virtual void updateAccount(const Account& account) = 0; 
     }; 

    AccountManager是需要被mock(模拟)的类。其中findAccountForUser(找到对应用户)和updateAccount(更新账户信息)是本次转账要mock的两个方法。注意,此处的两个方法用的是纯虚函数。因为这两个方法无需实现,测试的时候程序会走到mock出来的函数中,即使实现, 也不会被调用到。

    下面通过gmock来mock(模拟)AccountManager的两个方法:

     // MockAccountManager, mock AccountManager with googlemock 
     class MockAccountManager : public AccountManager 
     { 
     public: 
        MOCK_METHOD1(findAccountForUser, Account(const std::string&)); 
    
        MOCK_METHOD1(updateAccount, void(const Account&)); 
     }; 

    类MockAccountManager是继承数据库DB方法类AccountManager的。其中MOCK_METHOD1为gmock的宏定义。头文件中还有其他类似宏定义,如MOCK_METHOD0,MOCK_METHOD2...

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

    #2是你要mock的方法名称!#1表示你要mock的方法共有几个参数,#4是这个方法具体的参数,#3表示这个方法的返回值类型。很简单不是?!

    // Account.h 
     // basic application data class 
     #pragma once 
    
     #include <string> 
    
     class Account 
     { 
     private: 
        std::string accountId;  //账号
         
        long balance;           //存款 呃 还是叫余额吧
    
     public: 
        Account(); 
    
        Account(const std::string& accountId, long initialBalance); 
    
        //消费
        void debit(long amount); 
    
        //收入
        void credit(long amount); 
    
        //获取存款
        long getBalance() const; 
    
        //获取账号
        std::string getAccountId() const; 
     }; 

    上面这个类是为了方便模拟转账业务构造的一个账户类。类Account有debit(消费)、credit(收入)、getBalance(获取存款)和getAccountId(获取账号)四个方法。

    // A facility class acts as an external DB 
     class AccountHelper 
     { 
     private: 
        std::map<std::string, Account> mAccount; 
        // an internal map to store all Accounts for test 
    
     public: 
        AccountHelper(std::map<std::string, Account>& mAccount); 
    
        void updateAccount(const Account& account); 
    
        Account findAccountForUser(const std::string& userId); 
     }; 
    
     AccountHelper::AccountHelper(std::map<std::string, Account>& mAccount) 
     { 
        this->mAccount = mAccount; 
     } 
    
     void AccountHelper::updateAccount(const Account& account) 
     { 
        this->mAccount[account.getAccountId()] = account; 
     } 
    
     Account AccountHelper::findAccountForUser(const std::string& userId) 
     { 
        if (this->mAccount.find(userId) != this->mAccount.end()) 
            return this->mAccount[userId]; 
        else 
            return Account(); 
     } 

    上面这个类,扮演着数据库的就角色。mock(模拟)出来的方法,都将用该类的函数替代实现。

    下面,我们来写一个测试用例来看看gmock的神奇作用。

    // Test case to test AccountService 
     TEST(AccountServiceTest, transferTest) 
     { 
        std::map<std::string, Account> mAccount; 
        mAccount["A"] = Account("A", 3000); 
        mAccount["B"] = Account("B", 2000); 
        AccountHelper helper(mAccount); 
    
        MockAccountManager* pManager = new MockAccountManager(); 
    
        // specify the behavior of MockAccountManager 
        // always invoke AccountHelper::findAccountForUser 
         // when AccountManager::findAccountForUser is invoked 
        EXPECT_CALL(*pManager, findAccountForUser(testing::_)).WillRepeatedly( 
            testing::Invoke(&helper, &AccountHelper::findAccountForUser)); 
    
        // always invoke AccountHelper::updateAccount 
        //when AccountManager::updateAccount is invoked 
        EXPECT_CALL(*pManager, updateAccount(testing::_)).WillRepeatedly( 
            testing::Invoke(&helper, &AccountHelper::updateAccount)); 
    
        AccountService as; 
        // inject the MockAccountManager object into AccountService 
        as.setAccountManager(pManager); 
    
        // operate AccountService 
        as.transfer("A", "B", 1005); 
    
        // check the balance of Account("A") and Account("B") to 
        //verify that AccountService has done the right job 
        EXPECT_EQ(1995, helper.findAccountForUser("A").getBalance()); 
        EXPECT_EQ(3005, helper.findAccountForUser("B").getBalance()); 
    
        delete pManager; 
     } 

    首先,声明一个类MockAccountManager的实例pManager,在声明一个实现类AccountHelper实例helper。EXPECT_CALL的作用是将调用pManager方法替换成实现类helper的对应方法。此处,将pManager的findAccountForUser方法替换成helper的findAccount方法,将pManager的updateAccount方法替换成helper的updateAccount方法。

    最后,再用gtest的断言机制EXPECT_EQ来判断结果。

  • 相关阅读:
    Vue系列:.sync 修饰符的作用及使用范例
    Vue系列:Websocket 使用配置
    Vue系列:Slot 插槽的使用范例
    Vue系列:滚动页面到指定位置实现
    Vue系列:为不同页面设置body背景颜色
    Element UI系列:Upload图片自定义上传
    Vue系列:wangEditor富文本编辑器简单例子
    Element UI系列:Select下拉框实现默认选择
    sublime text 3 15个常用插件介绍
    基于iis下 wcf接口发布
  • 原文地址:https://www.cnblogs.com/peisy/p/4462506.html
Copyright © 2020-2023  润新知