亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

置換測試:Mock,Stub和其他

2019-11-14 18:37:16
字體:
來源:轉載
供稿:網友

簡介

在理想情況下,你所做的所有測試都是能應對你實際代碼的高級測試。例如,UI 測試將模擬實際的用戶輸入(Klaas 在他的文章中有討論)等等。實但際上,這并非永遠都是個好主意。為每個測試用例都訪問一次數據庫或者旋轉一次 UI 會使你的測試跑得非常慢,這會降低你的生產力,并導致你不去經常跑那些測試。若你測試的某段代碼依賴于網絡連接,這會要求你的測試環境具備網絡接入條件,而且這也難以模擬某些特殊的測試,比如當電話處于飛行模式情況下的時候。

正因如此,我們可以用一些模擬代碼替換你的實際代碼來編寫一些測試用例。

什么時候你會需要用到一些模擬 (mock) 對象呢?

讓我們從以下這些不同類型的模擬對象的基本定義開始。

double 可以理解為置換,它是所有模擬測試對象的統稱,我們也可以稱它為替身。一般來說,當你創建任意一種測試置換對象時,它將被用來替代某個指定類的對象。

stub 可以理解為測試樁,它能實現當特定的方法被調用時,返回一個指定的模擬值。如果你的測試用例需要一個伴生對象來提供一些數據,可以使用 stub 來取代數據源,在測試設置時可以指定返回每次一致的模擬數據。

spy 可以理解為偵查,它負責匯報情況,持續追蹤什么方法被調用了,以及調用過程中傳遞了哪些參數。你能用它來實現測試斷言,比如一個特定的方法是否被調用或者是否使用正確的參數調用。當你需要測試兩個對象間的某些協議或者關系時會非常有用。

mock 與 spy 類似,但在使用上有些許不同。spy 追蹤所有的方法調用,并在事后讓你寫斷言,而 mock 通常需要你事先設定期望。你告訴它你期望發生什么,然后執行測試代碼并驗證最后的結果與事先定義的期望是否一致。

fake 是一個具備完整功能實現和行為的對象,行為上來說它和這個類型的真實對象上一樣,但不同于它所模擬的類,它使測試變得更加容易。一個典型的例子是使用內存中的數據庫來生成一個數據持久化對象,而不是去訪問一個真正的生產環境的數據庫。

實踐中,這些術語常常用起來不同于它們的定義,甚至可以互換。稍后我們在這篇文章中會看到一些庫,它們自認為自己是 “mock 對象框架”,但是其實它們也提供 stub 的功能,而且驗證行為的方式也類似于我描述的 “spy” 而不是 “mock”。所以不要太過于陷入這些詞匯的細節;我下這些定義更多的是因為要在高層次上區分這些概念,并且它對考慮不同類型測試對象的行為會有幫助。

如果你對不同類型的模擬測試對象更多的細節討論感興趣,Martin Fowler 的文章 “Mocks Aren’t Stubs” 被認為是關于這個問題的權威討論。

模擬主義者 (Mockists) vs. 統計主義者 (Statists)

許多關于模擬對象的討論主要是衍生自 Fowler 的文章的,它們討論了兩種不同類型的程序員,模擬主義者和統計主義者,所寫的測試。

模擬主意的方式是測試對象之間的交互。通過使用模擬對象,你可以更容易地驗證被測對象是否遵循了它與其他類已建立的協議,使得在正確的時間發生正確的外部調用。對于那些使用行為驅動 (behavior-driven) 的開發者來說,這種測試可以驅動出更好的生產代碼,因為你需要明確模擬出特定的方法,這可以幫你設計出在兩個對象之間使用的更優雅的API,這種想法與模擬驅動緊密聯系在一起。因此模擬主義的測試更偏向于單元級別的測試,而不是完全的端到端 (end-to-end) 測試。

統計主義的方式是不使用模擬對象。這種思路是測試時只測試狀態而不是行為,因此這種類型的測試更加健壯。使用模擬測試時,如果你更新了實際類的行為,模擬類也需要同步更新;如果你忘了這么做,你可能會遇到測試可以通過但是代碼卻不能正確工作的情況。通過強調在測試環境中只使用那些真正的代碼,統計主意的測試可以幫助你減少測試代碼和實現代碼的耦合度,并降低出錯率。這種類型的測試,您可能已經猜到,適合于更全面的端到端的測試。

當然,并不是說有兩個對立的程序員學派;你不可能看到模擬主義和統計主義的當街對決。這種分歧是有用的,但是,得認識到 mock 在有些時候是你的工具箱里最好的工具,但是有時候又不是。不同類型的測試適用于不同的任務,并且最高效的測試套件往往是不同測試風格的集合體。仔細考慮你到底想要用單個測試來驗證些什么,這能幫助你找到最合適的測試方式,而且能幫你決定對于當前工作來說,使用模擬測試對象是否是正確的工具。

深入代碼

理論上談起來所有一切都沒什么問題,但讓我們來看一個你需要用到 mock 的真實用例。

讓我們試著測試一個對象,它上面有一個方法,是通過調用 UIapplication 的 openURL: 方法來打開另外一個應用程序。(這是我在測試我的 IntentKit 庫時遇到的一個真實問題。) 給這個用例寫一個端到端的測試,就算是有可能做到,也是非常困難的,因為 ‘成功狀態’ 本身導致了應用程序的關閉。自然的選擇是,模擬出一個 UIApplication 對象,并驗證這個模擬對象是否確實調用了 openURL 方法打開正確的 URL。

假設這個對象有這樣的方法:

@interface AppLinker : NSObject        - (instancetype)initWithApplication:(UIApplication *)application;        - (void)doSomething:(NSURL *)url;@end

這是一個非常牽強的例子,但是請容忍我一下。在這個例子中,你會注意到我們使用了構造方法進行注入,當我們創建 AppLinker 的對象時將 UIApplication 對象注入到其中。大部分情況下,使用模擬對象要求使用某種形式的依賴注入。如果這個概念對你很陌生,請一定看看本期的 Jon 的文章 中的描述。

OCMockito

OCMockito 是一個非常輕量級的使用模擬對象的庫:

UIApplication *app = mock([UIApplication class]);AppLinker *linker = [AppLinker alloc] initWithApplication:app];NSURL *url = [NSURL urlWithString:@"https://google.com"];[linker doSomething:URL];[verify(app) openURL:url];

OCMock

OCMock 是另一個 Objective-C 的模擬對象庫。和 OCMockito 類似,它提供了關于 stub 和 mock 的所有功能,并且包括了你可能需要的一切功能。它比 OCMockito 的功能更強,依賴于你的個人選擇,各有利弊。

在最基本層面上,我們可以使用 OCMock 來重寫出與之前非常類似的測試:

id app = OCMClassMock([UIApplication class]);AppLinker *linker = [AppLinker alloc] initWithApplication:app];NSURL *url = [NSURL urlWithString:@"https://google.com"];[linker doSomething:url];OCMVerify([app openURL:url]);

這種在你測試后再驗證調用方法的模擬測試風格被認為是一種 “運行后驗證” 的方式。OCMock 只在最近 3.0 版本后增加了對該功能的支持。同時它也支持老版本的風格,即對期望運行的驗證,在執行測試代碼前先設定對測試結果的期望。最后,你只需要驗證期望和實際結果是否對應:

id app = OCMClassMock([UIApplication class]);AppLinker *linker = [AppLinker alloc] initWithApplication:app];NSURL *url = [NSURL urlWithString:@"https://google.com"];OCMExpect([app openURL:url]);[linker doSomething:url];OCMVerifyAll();

Because OCMock lets you stub out class methods, you could also test this using OCMock, if your implementation of doSomething uses [UIApplication sharedApplication] rather than the UIApplication object injected in the initializer: 由于 OCMock 也支持對類方法的 stub,你也可以用這種方式來來測試,如果 doSomething 方法通過 [UIApplication sharedApplication] 來實現而不是 UIApplication 對象的注入初始化:

id app = OCMClassMock([UIApplication class]);OCMStub([app sharedInstance]).andReturn(app);AppLinker *linker = [AppLinker alloc] init];NSURL *url = [NSURL urlWithString:@"https://google.com"];[linker doSomething:url];OCMVerify([app openURL:url]);

你會發現 stub 類方法和 stub 實例方法看起來是一樣的。

構建你自己的測試

對于像這種簡單的用例,你也許不需要這么重量級的模擬對象測試庫。通常,你只需要創建你自己的模擬對象來測試你關心的行為:

@interface FakeApplication : NSObject    @PRoperty (readwrite, nonatomic, strong) NSURL *lastOpenedURL;    - (void)openURL:(NSURL *)url;@end@implementation FakeApplication    - (void)openURL:(NSURL *)url {        self.lastOpenedURL = url;    }@end

以下是測試:

FakeApplication *app = [[FakeApplication alloc] init];AppLinker *linker = [AppLinker alloc] initWithApplication:app];NSURL *url = [NSURL urlWithString:@"https://google.com"];[linker doSomething:url];XCAssertEqual(app.lastOpenedURL, url, @"Did not open the expected URL");

對于類似這個已經設計好的例子,就可能會出現這種情況,創造你自己的模擬對象只是增加了很多不必要的樣板,但如果你覺得需要模擬更為復雜的對象交互,那么完全控制模擬對象的行為就會非常有價值。

使用哪一個?

選擇哪一種方案完全依賴于你的具體測試情況以及你的個人偏好。OCMockito 和 OCMock 都可以通過 CocoaPods 安裝,將它們集成到你現有的測試環境都非常簡單,但需要注意的是,除非你需要,否則避免新增一些其他的依賴。另外除非真的需要,最好就都創建一些簡單的模擬對象。

模擬測試時的注意事項

在任何形式的測試中你有可能碰到的最大的問題之一是寫的測試和實現代碼耦合過于緊密。測試中一個最重要的關鍵點是降低未來的變化所帶來的成本;如果改變代碼的實現細節破壞了當前的測試,則這種成本已經增加了。也就是說,其實為了最小化由于使用模擬測試所造成不利影響,其實你有很多可以做的。

依賴注入是你的好伙伴

如果你還沒有使用依賴注入,或許你會需要它。雖然有時候不使用依賴注入來模擬對象也是可以的的 (比如以上面使用 OCMock 模擬類方法),但是通常是不太可能的。即使可能,設置測試所引入的復雜度也可能大于它能帶來的好處。如果你使用依賴注入的話,你會發現使用 stub 和 mock 方式寫測試要容易的多。

不要模擬你沒有的

許多有經驗的測試人員都會警告你“不要模擬你沒有的東西”,意思是你應該只為你代碼庫本身擁有的對象創建 mock 或 stub,而不是為第三方依賴或一些庫去創建。這里主要有兩個原因,一個是基于實際情況的,一個是更具有哲學性的考慮。

對于你的代碼庫,你對它不同接口的穩定性和不穩定性大概會有一個感覺,所以你可以通過你的直覺來判斷使用替換測試的方法是不是可能會導致測試過于脆弱。一般來說,你對第三方代碼沒有這樣的把握。為了解決這個問題,一個通用的做法是為第三方代碼創建包裝類來抽象出它的行為。在某些情況下,僅僅是轉移復雜性而不是降低復雜性往往是沒什么意義的。但是在一些情況下,你會很經常使用你的第三方代碼,這時這就是一個精簡你測試的好方法。你的單元測試能模擬出自定義對象,并使用高層次的集成或功能測試來測試你的包裝類本身。

iOS 和 OS X 開發世界的唯一性導致了事情稍微復雜一些。我們做的很多事情都依賴于 Apple 的框架,這個框架遠遠超過了其他語言的一些標準庫。雖然 NSUserDefaults 不是一個“你擁有”對象,但是,如果你發現你有需要把它模擬出來,那就放心去做吧,蘋果不太可能會在未來的 Xcode 的版本中推出打破這個 API 的變化。

另一個不要模擬第三方依賴庫的原因更具哲學性。使用模擬主義風格書寫測試的部分原因是通過這樣的測試能比較容易的找到兩個對象間最清晰可行的接口。但是如果是第三方依賴,你無法對其進行控制;API 協議中的一些詳細信息已經被第三方庫定死了,所以你無法通過測試來通過實驗有效地驗證接口是否有改進的余地。這本身不是問題,但在很多情況下,它降低了模擬測試的效果,直到把模擬測試的優點抹殺殆盡。

不要模仿我!

測試沒有銀彈;基于你的個人傾向和代碼的具體特性,不同的情況下需要使用不同的策略。測試替身可能不適用所有的情況,但它們會是你測試工具箱中一個非常有效的工具。不管你傾向于使用框架在單元測試中模擬出一切,還是只是根據需要創建你自己的模擬對象,當你思考如何測試你的代碼時,牢記模擬對象是非常有意義。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品中文久久久久久久| 日本成人在线视频网址| 日本免费在线精品| 国产精品免费视频xxxx| 欧美xxxx18性欧美| 91久久国产精品| 国产精品永久免费| 亚洲成人精品视频| 久久久久久国产三级电影| 亚洲人成欧美中文字幕| 亚洲欧美日韩一区在线| 亚洲欧洲激情在线| 欧美综合激情网| 国产69久久精品成人| 久久久精品国产网站| 日韩电影中文字幕| 久久精品久久久久久国产 免费| 日韩高清电影好看的电视剧电影| 成人免费大片黄在线播放| 亚洲色图美腿丝袜| 81精品国产乱码久久久久久| 欧美在线一区二区视频| 国产97在线视频| 777国产偷窥盗摄精品视频| 色婷婷综合久久久久| 欧美日韩亚洲精品内裤| 国产高清视频一区三区| 午夜精品一区二区三区在线视频| 久久综合五月天| 青草热久免费精品视频| 精品亚洲一区二区三区在线播放| 亚洲综合中文字幕在线观看| 欧美黄色三级网站| 97精品伊人久久久大香线蕉| 国产一区二区三区视频免费| 国产亚洲欧美日韩美女| 国产精品久久久久一区二区| 亚洲精品理论电影| 欧美亚洲在线播放| 精品福利樱桃av导航| 国产精品热视频| 性欧美视频videos6一9| 亚洲xxxx妇黄裸体| 欧美激情精品久久久久久大尺度| 欧美福利视频网站| 美日韩在线视频| 久久久精品日本| 欧美性生交大片免网| 成人乱人伦精品视频在线观看| 国产精品视频yy9099| 国产成人高清激情视频在线观看| 日韩免费观看高清| 国产成人精品一区二区三区| 日韩精品视频在线免费观看| 欧美美最猛性xxxxxx| 欧美性猛交xxxx免费看漫画| 亚洲aⅴ男人的天堂在线观看| 国产亚洲一区二区精品| 国产噜噜噜噜久久久久久久久| 国产精品欧美日韩一区二区| 在线午夜精品自拍| 欧美福利视频网站| 91欧美精品成人综合在线观看| 欧美三级欧美成人高清www| 日韩中文字幕精品视频| 国产精品综合久久久| 欧美色播在线播放| 国产成人小视频在线观看| 久久亚洲国产精品成人av秋霞| 在线性视频日韩欧美| 亚洲最大的网站| 欧美激情精品久久久久久免费印度| 久久精品美女视频网站| 日本中文字幕久久看| 国产精品福利在线观看| 欧美黑人极品猛少妇色xxxxx| 孩xxxx性bbbb欧美| 欧美激情啊啊啊| 国产成+人+综合+亚洲欧美丁香花| 精品国偷自产在线视频99| 亚洲精品美女在线| 亚洲自拍欧美另类| 亚洲自拍偷拍视频| 久久久精品免费| 国产日韩欧美综合| 欧美日本国产在线| 久久香蕉频线观| 国产一区二区日韩| 亚洲高清久久久久久| 国产日韩精品入口| 北条麻妃在线一区二区| 国产精品久久久久久久7电影| 一区二区三区国产在线观看| 日韩欧美中文字幕在线观看| 亚洲亚裔videos黑人hd| 在线成人免费网站| 成人免费网站在线看| 57pao国产成人免费| 欧美日韩激情视频8区| 国产精品福利网站| 久久综合免费视频影院| 国产精品视频久| 国产在线观看91精品一区| 91在线高清免费观看| 亚洲无亚洲人成网站77777| 欧美激情综合色综合啪啪五月| 伊是香蕉大人久久| 欧美成人免费全部| 国内精品视频久久| 亚洲一区美女视频在线观看免费| 国产精品美女主播在线观看纯欲| www亚洲精品| 68精品国产免费久久久久久婷婷| 欧美性生交大片免网| 日韩高清中文字幕| 成人免费在线视频网站| 国产精品99导航| 久久久久久久一区二区三区| 亚洲国产天堂久久综合网| 欧洲美女免费图片一区| 高跟丝袜欧美一区| 久久福利视频网| 国产美女被下药99| 亚洲男人的天堂网站| 国产精品一区二区久久久久| 91久久精品久久国产性色也91| 欧美成人亚洲成人日韩成人| 国产精品成人国产乱一区| 一级做a爰片久久毛片美女图片| 91九色国产社区在线观看| 欧美激情亚洲视频| 精品国产一区二区三区四区在线观看| 亚洲免费精彩视频| 成人国产精品久久久| 久久天天躁夜夜躁狠狠躁2022| 欧美亚洲一区在线| 亚洲二区在线播放视频| 色噜噜狠狠狠综合曰曰曰88av| 欧美尤物巨大精品爽| 国产一区二区三区精品久久久| 国产成人精品网站| 亚洲最大在线视频| 国产ts人妖一区二区三区| 欧美日韩裸体免费视频| 欧美精品www在线观看| 国产亚洲精品日韩| 在线观看国产成人av片| 亚洲一区二区免费在线| 亚洲午夜精品久久久久久久久久久久| 亚洲美女av在线播放| 一区二区三区亚洲| 欧美激情中文字幕在线| 成人国产精品一区二区| 日韩av影视在线| 国产成人精品久久二区二区91| 一区二区三区亚洲| 国产在线播放不卡| 在线观看日韩www视频免费| 午夜剧场成人观在线视频免费观看| 九九久久久久99精品| 国产精品久久久久久久久久东京| www.欧美精品| 91久久国产综合久久91精品网站| 精品久久久久久中文字幕大豆网|