mock 測試是單元測試的重要方法之一。本文介紹了基于 java 語言的 mock 測試架構 -- mockito 的使用。
mock 測試就是在測試過程中,對于某些不容易構造(如 httpservletrequest 必須在servlet 容器中才能構造出來)或者不容易擷取比較複雜的對象(如 jdbc 中的resultset 對象),用一個虛拟的對象(mock 對象)來建立以便測試的測試方法。
mock 最大的功能是幫你把單元測試的耦合分解開,如果你的代碼對另一個類或者接口有依賴,它能夠幫你模拟這些依賴,并幫你驗證所調用的依賴的行為。
比如一段代碼有這樣的依賴:
當我們需要測試a類的時候,如果沒有 mock,則我們需要把整個依賴樹都建構出來,而使用 mock 的話就可以将結構分解開,像下面這樣:
真實對象具有不可确定的行為,産生不可預測的效果,(如:股票行情,天氣預報) 真實對象很難被建立的 真實對象的某些行為很難被觸發 真實對象實際上還不存在的(和其他開發小組或者和新的硬體打交道)等等
使用一個接口來描述這個對象 在産品代碼中實作這個接口 在測試代碼中實作這個接口 在被測試代碼中隻是通過接口來引用對象,是以它不知道這個引用的對象是真實對象,還是 mock 對象。
關于這些架構的比較,不是本文的重點。本文着重介紹 mockito 的使用。
mockito 擁有的非常少的 api,所有開始使用 mockito,幾乎沒有時間成本。因為隻有一種創造 mock 的方式。隻要記住,在執行前 stub,而後在互動中驗證。你很快就會發現這樣 tdd java 代碼是多麼自然。
可以 mock 具體類而不單止是接口
一點注解文法糖 - <code>@mock</code>
幹淨的驗證錯誤是 - 點選堆棧跟蹤,看看在測試中的失敗驗證;點選異常的原因來導航到代碼中的實際互動。堆棧跟蹤總是幹幹淨淨。
允許靈活有序的驗證(例如:你任意有序 <code>verify</code>,而不是每一個單獨的互動)
支援“詳細的使用者号碼的時間”以及“至少一次”驗證
靈活的驗證或使用參數比對器的 stub (<code>anyobject()</code>,<code>anystring()</code> 或 <code>refeq()</code> 用于基于反射的相等比對)
gradle 使用者可以使用:
一旦建立 mock 将會記得所有的互動。你可以選擇驗證你感興趣的任何互動
預設情況下,所有方法都會傳回值,一個 mock 将傳回要麼 null,一個原始/基本類型的包裝值或适當的空集。例如,對于一個 int/integer 就是 0,而對于 boolean/boolean 就是 false。
stubbing 可以被覆寫。
一旦 stub,該方法将始終傳回一個 stub 的值,無論它有多少次被調用。
最後的 stubbing 是很重要的 - 當你使用相同的參數 stub 多次同樣的方法。換句話說:stubbing 的順序是重要的,但它唯一有意義的卻很少,例如當 stubbing 完全相同的方法調用,或者有時當參數比對器的使用,等等。
mockito 驗證參數值使用 java 方式:通過使用 equals() 方法。有時,當需要額外的靈活性,可以使用參數比對器:
如果你正在使用參數的比對,所有的參數都由比對器來提供。
下面的示例示範驗證,但同樣适用于 stubbing:
times(1) 是預設的,是以,使用的 times(1) 可以顯示的省略。
有序驗證是為了靈活 - 你不必一個接一個驗證所有的互動。
此外,您還可以通過建立 inorder 對象傳遞隻與有序驗證相關的 mock 。
注意:不建議 verifynomoreinteractions() 在每個測試方法中使用。 verifynomoreinteractions() 是從互動測試工具包一個友善的斷言。隻有與它的相關時才使用它。濫用它導緻難以維護。
最小化可重用 mock 建立代碼
使測試類更加可讀性
使驗證錯誤更加易讀,因為字段名稱用于唯一識别 mock
public class articlemanagertest {
在基礎類或者測試 runner 裡面,使用如下:
下面是一個精簡版本:
然而,這是不包括在最初的 mockito 另一個有争議的功能。我們建議您隻需用thenreturn() 或 thenthrow() 來 stubbing ,這在測試/測試驅動中應用簡潔與簡單的代碼足夠了。但是,如果你有一個需要 stub 到泛型 answer 接口,這裡是一個例子:
stubbing void 方法,需要不同的 when(object) ,因為編譯器不喜歡括号内無效的方法...
在 用于 stubbing void 方法中,dothrow(throwable...) 取代 stubvoid(object)。主要原因是提高可讀性和與 doanswer() 保持一緻性。
當你想用 stub void 方法 使用 dothrow():
在調用 when() 的相應地方可以使用 othrow(), doanswer(), donothing(), doreturn() 和 docallrealmethod(),當:
stub void 方法
stub 方法在 spy 對象(見下面)
可以不止一次的 stub 相同的方法,在測試的中期來改變 mock 的行為
但你更加傾向于使用這些方法來代替 when(),在所有的 stubbing 調用。可以閱讀更多關于這些方法的描述:
<a href="http://martinfowler.com/articles/mocksarentstubs.html">http://martinfowler.com/articles/mocksarentstubs.html</a>
<a href="http://mockito.org/">http://mockito.org/</a>