模擬對象

模擬對象

面向對象程式設計中,模擬對象(英語:mock object,也譯作模仿對象)是以可控的方式模擬真實對象行為的假的對象。程式設計師通常創造模擬對象來測試其他對象的行為,很類似汽車設計者使用碰撞測試假人模擬車輛碰撞中人的動態行為。

基本介紹

簡介,使用模擬對象原因,技術細節,模擬對象,虛擬對象和樁,設定預期結果,記錄日誌字元串,在測試驅動開發中的使用,局限性,

簡介

模擬對象運用對象的模擬版本來代替被依賴的類(dependent classes),這些模擬對象被傳遞給要測試的類後,依賴關係就被對象的模擬版本代替了,而被測試對象則仍然會以為自己所處理的是真實的對象。
模擬對象框架之前,模擬對象都是手工編寫的。
使用模擬對象的前提是:使用索依賴單元的接口必須定義清楚。

使用模擬對象原因

單元測試中,模擬對象可以模擬複雜的、真實的(非模擬)對象的行為, 如果真實的對象無法放入單元測試中,使用模擬對象就很有幫助。
在下面的情形,可能需要使用模擬對象來代替真實對象:
  • 真實對象的行為是不確定的(例如,當前的時間或當前的溫度);
  • 真實對象很難搭建起來;
  • 真實對象的行為很難觸發(例如,網路錯誤);
  • 真實對象速度很慢(例如,一個完整的資料庫,在測試之前可能需要初始化);
  • 真實的對象是用戶界面,或包括用戶界面在內;
  • 真實的對象使用了回調機制;
  • 真實對象可能還不存在;
  • 真實對象可能包含不能用作測試(而不是為實際工作)的信息和方法。
例如,一個可能會在特定的時間響鈴的鬧鐘程式可能需要外部世界的當前時間。要測試這一點,測試一直要等到鬧鈴時間才知道鬧鐘程式是否正確地響鈴。如果使用一個模擬對象替代真實的對象,可以變成提供一個鬧鈴時間(不管是否實際時間),這樣就可以隔離地測試鬧鐘程式。

技術細節

模擬對象具有和要模擬的真實對象的相同的接口,可以讓調用該接口的對象不知道在使用真實對象還是模擬對象。
現有的許多模擬對象框架允許程式設計師指定模擬對象上的哪些方法,將按照什麼順序被調用,以及傳入什麼參數,將返回什麼值。這樣,複雜對象(例如網路套接字)的行為將可以使用模擬對象來模擬,允許程式設計師來發現被測對象在可能各種存在的狀態是否回響正確。

模擬對象,虛擬對象和樁

一些作者明確區分虛擬對象(fake)和模擬對象。虛擬對象比較簡單,簡單實現所代表的對象相同的接口,並返回預先安排好的應答。這樣一來虛擬對象僅僅提供了一組方法樁。
在“單元測試的藝術”一書中,模擬對象被描述為幫助決定測試通過與否的虛擬對象,通過驗證對象上是否發生了互動。其他的都被定義為樁。在該書中,“虛擬對象”(fake)是指所有非真實的對象。基於使用,或者是樁,或者是模擬對象。
從這個角度講,模擬對象多做了一些工作:它們方法實現中包括斷言。這就是說,這個意義上的真正的模擬對象將會檢查每個調用的上下文— 可能會檢查器上方法的調用順序,可能對方法調用的參數數據進行檢驗。

設定預期結果

考慮一個授權子系統被模擬的例子。模擬對象與真正的授權類相同,實現了isUserAllowed(task: Task):boolean方法。如果暴露一個真實對象中沒有的屬性isAllowed: boolean就會帶來許多便利,測試代碼可以很容易地設定預期的結果,用戶通過授權,或沒有通過,這樣,兩種情況下可以很容易地測試系統的行為。
同樣,只有模擬對象才有的設定可以確保對子系統的後續調用將會導致異常拋出,或沒有反應的掛起,或返回null等。這樣,開發客戶端的行為時,可以對後端子系統中的所有實際的故障的條件以及預期的回響進行測試。沒有這樣簡單而靈活的模擬系統,對於每一種情形進行測試將是十分費力的。

記錄日誌字元串

一個模擬的資料庫對象的保存方法save(person: Person)可能包含許多(如果有)實現代碼。可能檢查存在與否,可能驗證要保存的Person對象(參見上述的虛擬對象與模擬對象的討論),但是除此以外可能沒有其他的實現代碼。
這就錯過了機會,模擬方法可以記錄一條日誌到公共的的日誌字元串。日誌可以簡單地寫“Person saved”,也可以寫person對象的詳細信息,如名字或ID。這樣,如果測試代碼在對模擬資料庫進行了一系列操作後檢查日誌字元串的最終內容,就能夠驗證資料庫保存方法的執行次數是否與預期相符。 這種方法可以發現不可見的性能問題,例如一個開發人員對丟失數據感到緊張,編寫了多次對save()的調用,而一次調用就已經足夠了。

在測試驅動開發中的使用

使用測試驅動開發(TDD)方法的程式設計師在編寫軟體時會使用模擬對象。模擬對象滿足更複雜的真實對象的接口需求,並代替真實對象的位置,有了模擬對象,程式設計師就可以對一個領域的功能性進行單元測試,而不需要實際調用複雜的下層或協作的。使用模擬對象使得開發人員可以關注與被測系統(SUT)的行為的測試,而不需要擔心被測系統的依賴關係。例如,測試在特定狀態下一個基於多個對象的複雜算法,如果使用模擬對象代替真實對象可以很容易地表達出來。
除了複雜性問題和關注點分離帶來的好處,還有實際的速度問題。使用測試驅動開發(TDD)開發一段實際的軟體很容易就有數百個單元測試。如果這些單元測試中許多都涉及到與資料庫,Web服務和其他進程間通訊網路系統的通訊,單元測試的組合會很快會慢到無法執行例行測試。而這會導致壞的習慣以及程式設計師不願意維護測試驅動開發的基本原則。
當模擬對象被替換為真實對象,端到端的功能仍需要進一步的測試。這將不再是單元測試,而是集成測試

局限性

模擬對象的使用可能會將單元測試與被測代碼的實現耦合得很緊。例如,許多模擬對象框架允許開發人員指定模擬對象上方法被調用的次序和調用的次數,這樣,測試通過後對代碼進行重構,即使方法依然遵守以前實現的契約,也可能會造成測試失敗。這說明單元測試應當測試方法的外部行為,而非其內部實現。在單元測試測試用例中過度使用模擬對象可能導致隨著系統的發展,不斷進行的重構會造成維護測試本身的工作量出現顯著的增長。在發展過程中,這種測試的不正確地維護可能會漏報錯誤,而在使用真實對象進行的測試中會捕捉到。相反,與設定好整個真實對象相比,簡單地模擬一個方法可能需要更少的配置,因此減少了需要的維護工作。
模擬對象必須要準確地建模它們要模擬的對象的行為,然而,如果要模擬的對象來自另一個開發人員或項目,或者如果還沒有開發出來,準確的建模是很難做到。如果沒有正確建模行為,那么可能會單元測試記錄通過,而真正運行時,在同樣條件下可能會造成測試失敗。

相關詞條

熱門詞條

聯絡我們