Google Mock启蒙篇 [1] (Google C++ Mocking Framework for Dummies 翻译)
Google C++ Mocking Framework for Dummies
Google Mock啟蒙篇
Version: 0.07< xmlnamespace prefix ="o" ns ="urn:schemas-microsoft-com:office:office" />
作者:adrian alexander
譯者:Koala++ /?屈偉
最新PDF版下載
What Is Google C++ Mocking Framework
當(dāng)你寫一個(gè)原型或是測(cè)試的時(shí)候,直接去依賴真實(shí)的對(duì)象通常是不可行的或是不明智的。Mock對(duì)象實(shí)現(xiàn)與真實(shí)對(duì)象有著相同的接口,但你可以去指定Mock對(duì)象在運(yùn)行時(shí)它做什么(?比如,調(diào)用哪個(gè)函數(shù),以什么順序,調(diào)用多少次,使用什么參數(shù),返回內(nèi)容是什么,等等?)。
注意:Fake對(duì)象和Mock對(duì)象兩個(gè)術(shù)語很容易混淆。在測(cè)試驅(qū)動(dòng)開發(fā)( TDD )語境下,Fake對(duì)象和Mock對(duì)象是區(qū)別很大的兩個(gè)概念。
l??Fakes是有具體實(shí)現(xiàn)的,但通常是一些走了捷徑的實(shí)現(xiàn),所以它不能真正的用于發(fā)布產(chǎn)品中。比如一個(gè)在內(nèi)存中的文件系統(tǒng)就是一個(gè)Fake的例子。
l??Mocks是一些有期望( expections )預(yù)先編程的對(duì)象,期望形成了有著期望結(jié)果的調(diào)用的一個(gè)特化。
上面所解釋也許太過抽象,但別擔(dān)心,最重要的就是記住:Mock可以使你檢查它與使用它的代碼之間的交互。其實(shí),當(dāng)你開始使用Mocks之后,Fakes和Mocks之間的區(qū)別會(huì)馬上清晰起來。
Google C++ Mocking Framework?(?或簡(jiǎn)稱Google Mock?)是一個(gè)用于創(chuàng)建Mock類使用這些類的庫(kù)(?有時(shí)我們也將它稱為框架,讓它聽起來更酷一些?)。它和Java世界中的jMock和EasyMock功能相同。
????使用Google Mock有下面三個(gè)基本步驟:
1.??使用簡(jiǎn)單的宏來描述你想Mock的接口,這些宏會(huì)自動(dòng)擴(kuò)展成你的Mock類的實(shí)現(xiàn)。
2.??創(chuàng)建一些Mock對(duì)象,并用一種直觀的語法去指定Mock對(duì)象的期望和行為。
3.??用這些Mock對(duì)象測(cè)試代碼。Google Mock會(huì)捕獲那些違反期望的沖突。
Why Google Mock?
????雖然Mock對(duì)象可以幫助你在測(cè)試中去除不必要的依賴,并使測(cè)試更快更可靠,但是在C++中用Mocks卻比較難。
l??它需要你自己去實(shí)現(xiàn)Mocks。這項(xiàng)工作通常無聊并且易錯(cuò)。無怪大家都盡可能不去做這種件事。
l??這些自己實(shí)現(xiàn)的Mocks質(zhì)量有一點(diǎn)……,嗯,不可預(yù)測(cè)。你可能見過一些寫的很精巧的Mocks,但你也可能看到一些匆忙間hack出來的Mocks,這些Mocks中充斥著各種怪異的限制。
l??你在使用一個(gè)Mock所獲得的經(jīng)驗(yàn)無法在下一個(gè)Mock中使用。
相反,Java和Python程序員有一些很好的Mock框架,它們可以自動(dòng)創(chuàng)建Mocks。結(jié)果,在Java和Python世界Mocking是被證明是一種高效的技術(shù),并被廣泛地使用。
Google Mock就是為幫助C++程序員解決Mock問題而生的。它受jMock和EasyMock的啟發(fā),但在設(shè)計(jì)時(shí)思慮了C++的特性。如果下面的任一問題困擾著你,那么Google Mock將成為你的朋友。
l??你被一個(gè)優(yōu)化了一部分的設(shè)計(jì)困住了,你希望可以做一些原型設(shè)計(jì),以免時(shí)間不夠了。但原型設(shè)計(jì)在C++中絕對(duì)不可能稱之為快。
l??你的測(cè)試很慢可能它們依賴很多庫(kù)或是使用很多資源(?比如,數(shù)據(jù)庫(kù)?)。
l??你的測(cè)試很脆弱,因?yàn)樗蕾嚨馁Y源不可靠。
l??你想測(cè)試你的代碼處理失敗的情況,但是很難產(chǎn)生一個(gè)失敗。
l??你需要保證你的模塊與別的模塊以正確的方式交互,但很難看到交互的過程,你只能看到最終運(yùn)行的結(jié)果,這無論如何都是尷尬的。
l??你想要“Mock out”你的依賴,但是這些依賴還沒有Mock實(shí)現(xiàn),坦白地講,就算是有,你看到這些手寫的Mocks也會(huì)頭痛。
我們推薦你在以下面的方式使用Google Mock:
l??作為一個(gè)設(shè)計(jì)工具,因?yàn)樗梢宰屇愀绺l繁地試驗(yàn)?zāi)愕慕涌谠O(shè)計(jì)。更多的迭代會(huì)產(chǎn)生更好的設(shè)計(jì)。
l??作為一個(gè)測(cè)試工具,它可以解除外圍的依賴,并可以查看你的模板與其它模塊的交互。
Get Started
????使用Google Mock很容易!在你的C++源文件中,只需要寫上#include “gtest/gtest.h”和“gmock/gmock.h”,你就可以開始你的Goole Mock之旅了。
A Case for Mock Turtles
讓我們看一個(gè)例子。假設(shè)你在開發(fā)一個(gè)圖形程序,它依賴一個(gè)類似Logo(譯注:初一我學(xué)的第一門計(jì)算機(jī)語言,每次我聽到它名字都會(huì)激動(dòng)萬分,雖然它的命令我?guī)缀跬饬?/span>?)的API來繪圖,你怎么去測(cè)試你的程序是正確的呢?嗯,你可以運(yùn)行它,然后比較你屏幕上的結(jié)果和目標(biāo)屏幕截圖,但是必需要承認(rèn)的是:這種測(cè)試很麻煩,并且健壯性不足(?如果你升級(jí)了你的顯卡,這個(gè)顯卡有更好的抗鋸齒能力,那你需要把你用的圖形文件都換了?)。如果你的測(cè)試都是這樣的,那你會(huì)很痛苦的。幸運(yùn)的是,你知道依賴注入并且知道該如何去做:不要讓你的程序直接去調(diào)用繪圖API,而應(yīng)該將API封裝成一個(gè)接口( Turtle,譯注:Logo語言中的圖標(biāo)像是一個(gè)海龜,在Doc時(shí)代這完全是騙小朋友的,它就是一個(gè)沒有尾巴的箭頭?),并針對(duì)接口編程。
class?Turtle?{
??...
??virtual?~Turtle() {}
??virtual?void?PenUp() = 0;
??virtual?void?PenDown() = 0;
??virtual?void?Forward(int?distance) = 0;
??virtual?void?Turn(int?degrees) = 0;
??virtual?void?GoTo(int?x,?int?y) = 0;
??virtual?int?GetX()?const?= 0;
??virtual?int?GetY()?const?= 0;
};
注意:Turtle類的析構(gòu)函數(shù)必須是虛函數(shù),因?yàn)樵陔S后的介紹中要繼承這個(gè)類。
????你可以通過PenUp()和PenDown()來控制光標(biāo)的移動(dòng)是否會(huì)留下痕跡,并用Forward(),Turn(),和Goto()來控制它的移動(dòng),GetX()和GetY()會(huì)告訴你當(dāng)前光標(biāo)的位置。
????你的程序通常會(huì)使用這個(gè)接口的真實(shí)實(shí)現(xiàn)。但在測(cè)試中,你可以使用一個(gè)Mock實(shí)現(xiàn)來代替。這可以讓你更早地檢查你的程序調(diào)用哪些繪圖API,使用什么參數(shù),以什么順序調(diào)用。這樣的測(cè)試更健壯(?這樣的測(cè)試不會(huì)因?yàn)槟愕男嘛@卡在反鋸齒性上表現(xiàn)不同而失敗?),并且這種代碼更容易去理解和維護(hù)(?測(cè)試的目標(biāo)是用代碼表示,而不是用一些二進(jìn)制圖形去表示),而且會(huì)運(yùn)行的非常非常快。
Writing the Mock Class
????如果你需要的Mocks已經(jīng)有好心人實(shí)現(xiàn)了,那你很走運(yùn)。但是如果你發(fā)現(xiàn)需要自己要去實(shí)現(xiàn)Mock類,也別緊張,Google Mock已經(jīng)將這個(gè)任務(wù)變成了一個(gè)有趣的游戲(?嗯,算是吧?)。
How to Define It
這里以Turtle接口為例子,下面是你需要去做的幾個(gè)步驟:
1.??繼承Turtle類得到MockTurtle類。
2.??從Turtle類中選一個(gè)虛函數(shù)(?也可用模板Mock非虛函數(shù),但那涉及的知識(shí)就多了一些?),數(shù)一下這個(gè)函數(shù)有幾個(gè)參數(shù)。
3.??在MockTurtle的public:部分,寫上MOCK_METHODn(); (如果你要Mock一個(gè)const函數(shù),就寫MOCK_CONST_METHODn ),其中n是函數(shù)中的參數(shù)個(gè)數(shù),如果你真的連數(shù)數(shù)都能數(shù)錯(cuò),那編譯器會(huì)坦白地告訴你這個(gè)丟臉的事實(shí)。
4.??這一步終于是能看到意義的一步了:你把函數(shù)名作為宏的第一個(gè)參數(shù),然后將函數(shù)定義中除函數(shù)名以外的部分作為宏的第二個(gè)參數(shù)。
5.??重復(fù)上述步驟,直到你想Mock的虛函數(shù)都Mock了。
在完成上述步驟后,你得到的是類似下面的代碼:
#include?"gmock/gmock.h"??// Brings in Google Mock.
class?MockTurtle?:?public?Turtle?{
?public:
??...
??MOCK_METHOD0(PenUp,?void());
??MOCK_METHOD0(PenDown,?void());
??MOCK_METHOD1(Forward,?void(int?distance));
??MOCK_METHOD1(Turn,?void(int?degrees));
??MOCK_METHOD2(GoTo,?void(int?x,?int?y));
??MOCK_CONST_METHOD0(GetX,?int());
??MOCK_CONST_METHOD0(GetY,?int());
};
????你不需要再在別的地方去定義這些Mock函數(shù)了,MOCK_METHOD*宏會(huì)幫你產(chǎn)生這些函數(shù)定義。這很簡(jiǎn)單!一旦掌握了它的訣竅,你可以產(chǎn)生大量的Mock類,可能快到連源代碼管理工具都處理不過來。
小提示:如果連定義對(duì)你來說工作量都太大,你可以在scripts/generator目錄下找到一個(gè)gmock_gen.py工具,這個(gè)命令行工具需要安裝Python 2.4。你將C++文件名和抽象類名作為參數(shù)傳入這個(gè)工具,它會(huì)打印Mock類的定義給你。但是因?yàn)?/span>C++的復(fù)雜性,這個(gè)腳本還是可能出錯(cuò),但當(dāng)它不出錯(cuò)的時(shí)候,還是很方便的。更多的細(xì)節(jié)在用戶文檔中。
Where to Put It
????當(dāng)你定義一個(gè)Mock類,你需要決定把它的定義放到哪。一些人把它放到一個(gè)*_test.cc文件中。當(dāng)這個(gè)接口(就叫Foo吧)是由同一個(gè)人或是同一團(tuán)隊(duì)維護(hù)時(shí),這沒什么問題。但如果不是,當(dāng)Foo的維護(hù)者修改了它,你的測(cè)試就會(huì)編譯不通過(?你總不能指望Foo的維護(hù)者去修改每個(gè)使用Foo的測(cè)試測(cè)試吧?)。
????所以,經(jīng)驗(yàn)法則是:如果你需要Mock Foo并且它由別人維護(hù)時(shí),在Foo包中定義Mock類(?更好的做法是在測(cè)試包中定義它,這樣可以將測(cè)試代碼更清晰地獨(dú)立出來),把它放到mock_foo.h中。那么每個(gè)想使用Mock Foo類的都可以在他們的測(cè)試代碼中引用它。如果Foo改變了,那么只需要改一份MockFoo的代碼,并且只有依賴這個(gè)變動(dòng)函數(shù)的測(cè)試代碼需要做相應(yīng)的修改。
????另一種做法是:你可以在Foo之上引入一個(gè)FooAdaptor層,并針對(duì)FooAdaptor這個(gè)新接口編程。因?yàn)槟銓?duì)FooAdaptor有控制權(quán),你可以很容易地將Foo的改變隱藏掉。雖然這意味著在開始有更大的工作量,但認(rèn)真構(gòu)造的適配器接口會(huì)使你的代碼更容易開發(fā),也有更高的可讀性,因?yàn)槟銟?gòu)造的適配器接口FooAdaptor會(huì)比Foo更適合于你的特定領(lǐng)域開發(fā)。
Using Mocks in Tests
????當(dāng)你完成Mock類的定義之后,使用它是很簡(jiǎn)單的。典型的流程如下:
1.??引用那些你需要使用的Google Mock有關(guān)的命名空間(?這樣你就不用每次都把命名空間加到前面,請(qǐng)牢記,使用命名空間是一個(gè)好主意,并且對(duì)你的健康大有裨益?)。
2.??創(chuàng)建一些Mock對(duì)象。
3.??對(duì)它們指定你的期望(?一個(gè)函數(shù)要被調(diào)用多少次??用什么參數(shù)??它返回什么??等等?)。
4.??用這些Mocks來測(cè)試一些代碼。你可以選擇Google Test Assertions來檢查返回。如果一個(gè)Mock函數(shù)被調(diào)用次數(shù)多于期望,或是使用了錯(cuò)誤的參數(shù),你會(huì)馬上得到一個(gè)錯(cuò)誤?提示。
5.??當(dāng)一個(gè)Mock對(duì)象被析構(gòu)時(shí),Google Mock會(huì)自動(dòng)檢查在它上面的所有的期望是否都已經(jīng)滿足了。
下面是一個(gè)例子:
#include?"path/to/mock-turtle.h"
#include?"gmock/gmock.h"
#include?"gtest/gtest.h"
using?::testing::AtLeast;?????????????????????// #1
?
TEST(PainterTest, CanDrawSomething) {
??MockTurtle turtle;??????????????????????????// #2
??EXPECT_CALL(turtle, PenDown())??????????????// #3
??????.Times(AtLeast(1));
?
??Painter painter(&turtle);???????????????????// #4
?
??EXPECT_TRUE(painter.DrawCircle(0, 0, 10));
}?????????????????????????????????????????????// #5
?
int?main(int?argc,?char** argv) {
??// The following line must be executed to initialize Google Mock
??// (and Google Test) before running the tests.
??::testing::InitGoogleMock(&argc, argv);
??return?RUN_ALL_TESTS();
}
????正如你所猜測(cè)的一樣,這個(gè)測(cè)試是檢查PenDown()是否被調(diào)用了至少一次。如果Painter對(duì)象并沒有調(diào)用這個(gè)函數(shù),你的測(cè)試就會(huì)失敗,提示信息類似如下:
path/to/my_test.cc:119: Failure
Actual function call count doesn't match this expectation:
Actually: never called;
Expected: called at least once.
技巧1:如果你從一個(gè)Emacs Buffer運(yùn)行這個(gè)測(cè)試程序,你可以在錯(cuò)誤信息的行號(hào)上敲Enter鍵,就可以直接跳到期望失敗的那一行了。
技巧2:?如果你的Mock對(duì)象永不釋放,最后的檢查是不會(huì)發(fā)生的。所以當(dāng)你在堆上分配Mock對(duì)象時(shí),你用內(nèi)存泄露工具檢查你的測(cè)試是一個(gè)好主意(譯注:推薦valgrind )。
重要提示:Google Mock要求期望在Mock函數(shù)被調(diào)用之前就設(shè)置好,否則行為將是未定義的。特別是你絕不能在Mock函數(shù)調(diào)用中間插入EXPECT_CALL()。
這意味著EXPECT_CALL()應(yīng)該被理解為一個(gè)調(diào)用在未來的期望,而不是已經(jīng)被調(diào)用過函數(shù)的期望。為什么Google Mock要以這種方式工作呢?嗯……,在前面指定期望可以讓Google Mock在異常發(fā)生時(shí)馬上可以提示,這時(shí)候上下文(?棧信息,等等?)還是有效的。這樣會(huì)使調(diào)試更容易。
要承認(rèn)的是,這個(gè)測(cè)試沒有展示出Google Mock有什么強(qiáng)大之處。你完全可以不用Google Mock來得到相同的效果。但是別急,在下面的展示中,我會(huì)讓你看到Google Mock的強(qiáng)大,它可以讓你用Mock做非常多的事。
Using Google Mock with Any Testing Framework
如果你在用別的測(cè)試框架而不是Google Test(?比如,CppUnit或CxxUnit?),只需要把上節(jié)中的main函數(shù)改成下面這樣:
int?main(int?argc,?char** argv) {
??// The following line causes Google Mock to throw
// an exception on failure, which will be interpreted
// by your testing framework as a test failure.
??::testing::GTEST_FLAG(throw_on_failure) =?true;
??::testing::InitGoogleMock(&argc, argv);
??... whatever your testing framework requires ...
}????
????這種方法中有一個(gè)catch:它可以讓Google Mock從Mock對(duì)象的析構(gòu)函數(shù)中拋出一個(gè)異常。但有一些編譯器,這會(huì)讓測(cè)試程序崩潰(?譯注:可以參考Effect C++第三版的Item 8)。雖然你仍然可以注意到注意失敗了,但這絕不是一個(gè)優(yōu)雅的失敗方式。
????一個(gè)更好的方法是用Google Test的event listener API來以合理的方式報(bào)告一個(gè)測(cè)試失敗給你的測(cè)試框架。你需要實(shí)現(xiàn)OnTestPartResult()函數(shù)這個(gè)事件監(jiān)聽接口,但實(shí)現(xiàn)它也很簡(jiǎn)單。
????如果上面的方法對(duì)你來說工作量太大,我建議你還是用Google Test吧,它與Google Mock可以無縫結(jié)合。如果你有什么Google Test滿足不了你測(cè)試需求的原因,請(qǐng)告訴我們。
總結(jié)
以上是生活随笔為你收集整理的Google Mock启蒙篇 [1] (Google C++ Mocking Framework for Dummies 翻译)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Google Mock启蒙篇 [2] (
- 下一篇: GDB 单步调试