白盒測試之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來判斷結果。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM