本文目的
使用gmock時,有時候需要向被mock的函數輸入一些自定以參數,用來測試調用mock的方法的各種的執行情況。前幾天在項目中遇到這個問題,並使用了該技巧,覺得很有用,所以總結出來,作為備忘。
一個例子
XXXClient是一個類,用於和遠端服務器交互,內部實現涉及網絡通訊。
class XXXClient { public: void QueryXXX(const Request&, Response&); };
內部有個函數QueryXXX,用於執行查詢操作。查詢的響應對象通過引用的方式傳回給調用對象,避免不必要的對象拷貝。
現在有個類,會調用XXXClient::QueryXXX方法,如下面所示:
class XXXRunner { public: QueryResult DoSomething(XXXClient& oClient) { Request oReq; … // 設置oReq Response oRsp; oClient.QueryXXX(oReq, oRsp); // 這里以后的邏輯需要測試 … // 根據oRsp的內容,做一些相關邏輯,並創建QueryResult對象 return oResult; } };
現在希望做的是在測試用例中設置各種輸出(oRsp),測試調用QueryXXX之后的代碼邏輯,更重要的是,不希望為了執行單元測試,專門搭建一個服務器與XXXClient交互,成本太高。
Gmock的解決方法
gmock內部實現了一系列Action的宏,可以幫我們完成上面的需求。先看看代碼,首先mock我們的XXXClient,
class XXXClientMock : public XXXClient { public: MOCK_METHOD2(QueryXXX, QueryResult (Request&, Response&)); };
Mock的XXXClient后,我們就可以使用mock對象在單元測試中測試了。
TEST(XXXRunnerTC, SetArgRefereeDemo) { XXXCLientMock oMock; // 設計一個期望的oRsp返回對象 Response oRsp; oRsp.attr1 = “…”; oRsp.attr2 = “any thing you like”; // 關聯oRsp與mock對象的Query EXPECT_CALL(oMock, QueryXXX(_, _)). WillOnce(SetArgReferee<1>(oRsp)); // OK 設置完畢 // 調用QueryXXX XXXRunner oRunner; QueryResult oRst = oRunner.DoSomething(oMock); … // 設置斷言 EXPECT_EQ(“abcdefg”, oRst.attr1); …… }
Gmock的Action
好了,現在解決了本文開始提出的問題,可以捎帶做一點擴展閱讀。上面用的一些API,在Gmock中稱之為Action(行為),主要目的是方便快速的為mock類提供測試行為,用於測試。上面,填充oRsp對象就是用SetArgReferee<N>(value)行為實現的,通過名字,可以知道這個行為對象是用於設置參數,N是參數的索引,從0開始。類似的行為還有SetArgPointee<N>(value),設置指針參數, SetArrayArgument<N>(first, last),設置數組參數,數組范圍[first,last)。
設置返回值,可以使用行為Return(Value)等一些列API,更詳細的可以閱讀最后的“參考資料中的gmock cheat sheet”。
有時候,可能需要設置參數和返回值,也就是同時設置多個行為,那么可以使用DoAll函數幫我們實現,DoAll相當於一個action的集合,使用示例如下:
… EXPECT_CALL(oMock, QueryXXX(_, _)). WillOnce(DoAll(SetArgReferee<1>(oRsp), Return(someObj)))); …
還可以通過Invoke行為,將負責的測試行為添加進來。
總結
用了Gtest&Gmock框架一段時間了,發現使用之他們之后,的確可以提升代碼質量,並且加快bug定位,從某個側面來說,提高的編碼效率。現在,不論用什么語言寫程序,在熟悉了語言本身之后,第一件事就是去熟悉這門語言相關的單元測試框架。
個人認為寫單元測試不只是一種行為,更是一種信仰。
參考資料
- Gmock cheat sheet: http://code.google.com/p/googlemock/wiki/CheatSheet
- Gmock cookbook: http://code.google.com/p/googlemock/wiki/CookBook
- Stackoverflow: http://stackoverflow.com/questions/8845753/how-to-set-custom-ref-variable-in-gmock