java powermock - Mockito與JMockit之間的比較-為什麼Mockito投票比JMockit更好?




unit-testing mocking (5)

我曾與Mockito和JMockit合作過,我的經驗是:

  • 的Mockito:

    • 隱式嘲諷( - >更好的可用性,但有可能無法檢測到不允許的模擬方法調用)
    • 明確的驗證
  • EasyMock的:

    • 表達嘲笑
    • 隱式驗證
  • JMockit:

    • 支持兩者
  • 此外,JMockit的其他好處:

    • 如果你正在嘲笑靜態方法/構造函數等(比如擴展一個非UT的舊代碼),你將有兩種選擇:1)帶有Powermock擴展的Mockito / EasyMock或者2)Jmockit
    • 內置覆蓋報告

我個人更喜歡JMockit,我認為它更具功能豐富和靈活性,但需要一些更陡峭的學習曲線。 通常有多種方法可以達到相同的模擬效果,並且在設計模擬時需要更多的關注。

我正在研究為我的項目使用哪個模擬框架,並將其縮小到JMockitMockito

我注意到Mockito被評為“ Java最佳模擬框架 ”。
在比較JMockit的“ 模擬工具比較矩陣 ”中的功能時, JMockit顯示出多種不同的功能。

有沒有人有任何具體的信息(而不是意見) Mockito可以做什麼,這是JMockit無法實現的,反之亦然?


為了便於測試我們的遺留代碼庫(有很多靜態方法調用等),JMockit具有無價的價值。 [我的博客article無恥插件]


我想說這場比賽是在JMockitPowerMock之間,然後是Mockito

我會留下“普通”的jMock和EasyMock,因為它們只使用代理和CGLIB,並且不像新的框架那樣使用Java 5工具。

jMock也沒有穩定的版本超過4年。 jMock 2.6.0需要2年的時間才能從RC1發展到RC2,然後在實際發布之前的2年。

關於Proxy&CGLIB與儀器:

(EasyMock和jMock)基於java.lang.reflect.Proxy,它需要實現一個接口。 另外,它們支持通過CGLIB子類生成類的模擬對象。 因此,所述類不能是最終的,只有可覆蓋的實例方法可以被模擬。 然而,最重要的是,在使用這些工具時,測試中的代碼的依賴關係(也就是說,測試中給定的類所依賴的其他類的對象)必須由測試控制,以便可以將mock實例傳遞給客戶端這些依賴關係。 因此,依賴關係不能簡單地用我們想寫單元測試的客戶類中的新運算符實例化。

最終,傳統嘲笑工具的技術局限性對生產代碼施加以下設計限制:

  1. 每個可能需要在測試中被模擬的類都必須實現一個單獨的接口,或者不是最終的。
  2. 要測試的每個類的依賴關係必須通過可配置的實例創建方法(工廠或服務定位器)獲得,或者通過依賴注入來公開。 否則,單元測試將無法將依賴關係的模擬實現傳遞給被測單元。
  3. 由於只有實例方法可以被模擬,所以要進行單元測試的類不能在它們的依賴項上調用任何靜態方法,也不能使用任何構造函數實例化它們。

以上內容從http://jmockit.org/about.html複製而來。 此外,它以幾種方式比較自身(JMockit),PowerMock和Mockito:

現在還有其他針對Java的嘲諷工具,它們也克服了傳統工具的局限性,包括PowerMock,jEasyTest和MockInject。 最接近JMockit特性集的是PowerMock,所以我將在這裡簡要評估它(另外,其他兩個更受限制,並且不再被積極開發)。

JMockit與PowerMock

  • 首先,PowerMock不提供用於模擬的完整API,而是作為另一種工具的擴展,其目前可以是EasyMock或Mockito。 這顯然是這些工具的現有用戶的優勢。
  • 另一方面,JMockit提供了全新的API,儘管其主要API(Expectations)與EasyMock和jMock類似。 雖然這會創建更長的學習曲線,但它還允許JMockit提供更簡單,更一致且更易於使用的API。
  • 與JMockit Expectations API相比,PowerMock API更“低級”,強制用戶找出並指定需要為測試準備哪些類(使用@PrepareForTest({ClassA.class,...})註釋)並需要特定的API調用來處理可能存在於生產代碼中的各種語言結構:靜態方法(mockStatic(ClassA.class)),構造函數(suppress(constructor(ClassXyz.class))),構造函數調用expectNew(AClass.class)),部分模擬(createPartialMock(ClassX.class,“methodToMock”))等。
  • 通過JMockit Expectations,各種方法和構造函數都以純粹的聲明方式被模擬,部分模仿通過@Mocked註釋中的正則表達式指定,或者通過簡單地“取消模擬”沒有預期記錄的成員; 也就是說,開發人員只需為測試類聲明一些共享的“模擬字段”,或為個別測試方法聲明一些“本地模擬字段”和/或“模擬參數”(在最後一種情況下,@Mocked註釋通常不會被需要)。
  • PowerMock目前不支持JMockit中提供的一些功能,例如支持模擬equals和hashCode,重寫方法等等。 此外,沒有等同於JMockit在測試執行時捕獲指定基類型的實例和模擬實現的能力,而測試代碼本身沒有任何實際實現類的知識。
  • PowerMock使用自定義類加載器(通常每個測試類一個)來生成修改後的類的類。 如此大量使用自定義類加載器可能會導致與第三方庫衝突,因此有時需要在測試類上使用@PowerMockIgnore(“package.to.be.ignored”)註釋。
  • 雖然在JDK 1.5上開發時需要將“-javaagent”參數傳遞給JVM,但JMockit(通過“Java代理”運行時檢測)所使用的機制更簡單且更安全; 在JDK 1.6+上(即使部署在較早版本上,始終可用於開發),因為JMockit可以通過使用Attach API按需透明地加載Java代理,所以不存在這樣的要求。

另一個最近的嘲笑工具是Mockito。 雖然它不試圖克服舊工具(jMock,EasyMock)的局限性,但它引入了一種新的mock行為測試方式。 JMockit還通過驗證API支持這種替代風格。

JMockit vs Mockito

  • Mockito依賴對其API的顯式調用,以便將記錄(當(...))和驗證(驗證(...))階段之間的代碼分開。 這意味著在測試代碼中對模擬對象的任何調用也需要調用模擬API。 另外,當(...)和驗證(模擬)...呼叫時,這通常會導致重複。
  • 使用JMockit,不存在類似的調用。 當然,我們有新的NonStrictExpectations()和新的Verifications()構造函數調用,但它們每次測試只發生一次(通常),並且完全獨立於對模擬方法和構造函數的調用。
  • Mockito API在用於調用模擬方法的語法中包含若干不一致之處。 在記錄階段,我們調用了像when(mock.mockedMethod(args))...而在驗證階段,這個相同的調用將被寫為verify(mock).mockedMethod(args)。 請注意,在第一種情況下,對mockedMethod的調用直接在模擬對像上進行,而在第二種情況下,它對由verify(mock)返回的對象進行調用。
  • JMockit沒有這樣的不一致性,因為對模擬方法的調用總是直接在模擬實例本身上進行。 (只有一個例外:為了匹配相同模擬實例上的調用,使用onInstance(mock)調用,導致代碼如onInstance(mock).mockedMethod(args);儘管如此,大多數測試不需要使用它。 )
  • 就像其他依賴方法鏈接/包裝的嘲諷工具一樣,當剔除void方法時,Mockito也會遇到不一致的語法。 例如,你寫的時候(mockedList.get(1))。thenThrow(new RuntimeException()); 對於一個非void方法,以及doThrow(new RuntimeException())。when(mockedList).clear(); 為無效的。 使用JMockit,它始終是相同的語法:mockedList.clear(); result = new RuntimeException();.
  • 然而,在使用Mockito間諜方面還存在另一個不一致:“嘲笑”允許真正的方法在窺探的實例上執行。 例如,如果間諜指的是一個空的List,那麼不用寫when(spy.get(0))。thenReturn(“foo”),你需要編寫doReturn(“foo”)。when(spy).get 0)。 通過JMockit,動態模擬功能為間諜提供了類似的功能,但沒有這個問題,因為真正的方法只能在重放階段執行。
  • 在EasyMock和jMock中,第一個用於Java的模擬API,重點完全在於記錄模擬對象的預期調用,對於默認情況下不允許意外調用的模擬對象。 這些API還為允許意外調用的模擬對象提供允許調用的記錄,但這被視為二級功能。 此外,使用這些工具,在測試代碼被執行後,無法顯式驗證調用模擬。 所有這些驗證都是隱含和自動執行的。
  • 在Mockito(也在Unitils Mock中),採取了相反的觀點。 在測試過程中模擬對象的所有調用,無論是否記錄,都是允許的,從來沒有預料到。 在被測代碼被執行後,驗證是明確執行的,而不是自動執行。
  • 這兩種方法都太過於極端,因此並不理想。 JMockit Expectations&Verifications是唯一允許開發人員無縫選擇嚴格(默認情況下預期)和非嚴格(默認情況下允許)每種測試的模擬調用的最佳組合的API。
  • 更清楚的是,Mockito API有以下缺點。 如果您需要驗證在測試過程中是否發生了對非空模擬方法的調用,但測試需要該方法的返回值與返回類型的默認值不同,那麼Mockito測試將具有重複的代碼: (mock.someMethod())。然後返回(xyz)調用記錄階段,並在驗證階段驗證(模擬).someMethod()。 使用JMockit,可以始終記錄嚴格的期望,而不必進行明確的驗證。 或者,可以為任何記錄的非嚴格期望指定調用次數約束(次數= 1)(對於Mockito,這種約束只能在驗證(模擬,約束)調用中指定)。
  • Mockito對於驗證按順序進行的語法很差,並且對於全面驗證(即,檢查所有對模擬對象的調用都進行了明確驗證)。 在第一種情況下,需要創建一個額外的對象,並調用驗證對象:InOrder inOrder = inOrder(mock1,mock2,...)。 在第二種情況下,需要調用像verifyNoMoreInteractions(mock)或verifyZeroInteractions(mock1,mock2)。
  • 使用JMockit,您只需編寫新的VerificationsInOrder()或新的FullVerifications()而不是新的驗證()(或新的FullVerificationsInOrder()來組合這兩個需求)。 無需指定涉及哪些模擬對象。 沒有額外的模擬API調用。 作為獎勵,通過在有序的驗證塊中調用unverifiedInvocations(),您可以執行Mockito中根本不可能的訂單相關驗證。

最後,JMockit Testing Toolkit比其他模擬工具包具有更廣泛的範圍更宏大的目標 ,以便提供完整和復雜的開發人員測試解決方案。 嘲諷的好API,即使沒有人為限制,也不足以用於生產性測試。 與IDE無關,易於使用且集成的代碼覆蓋工具也非常重要,這就是JMockit Coverage旨在提供的功能。 隨著測試套件規模的擴大,另一塊開發人員測試工具集將變得更加有用,它能夠在對生產代碼進行本地化更改之後增量地重新運行測試; 這也包含在Coverage工具中。

(授予,源可能有偏見,但是...)

我會說與JMockit一起去。 這是最簡單易用的,靈活的,適用於幾乎所有的情況下,甚至是困難的情況下,當你無法控制要測試的類時(或者由於兼容性原因你不能破壞它)。

我對JMockit的經歷非常積極。


使用jMockit,因為它是Deencapsultation.class中的反射庫。 我真的很喜歡Mockito的風格,但我拒絕改變我的代碼,並且渾濁了我的API,只有有限的測試框架才能實現它。 我很喜歡測試我所有的代碼,所以一個不能輕易測試私有方法的框架並不是我想要使用的。

我被這篇文章所左右

經過(不可否認的是很大的)學習曲線之後,jMockit現在成為我主要的mock單元測試框架。


官方的答案是來自

  1. 英特爾 - 避免分支機構錯誤預測的成本
  2. 英特爾 - 分支和循環重組以防止錯誤預測
  3. 科學論文 - 分支預測計算機結構
  4. 書籍:JL Hennessy,DA Patterson:計算機體系結構:定量方法
  5. 科學出版物中的文章:TY Yeh,YN Patt在分支預測中做了很多這些。

您還可以從這個可愛的diagram看到為什麼分支預測器會混淆。

原始代碼中的每個元素都是隨機值

data[c] = std::rand() % 256;

因此,預測因素將會改變方向std::rand()

另一方面,一旦它被排序,預測器將首先進入強烈未被採用的狀態,並且當值變為高值時,預測器將在三次運行中通過從強烈不採取到強烈採取的一直改變。





java unit-testing mocking mockito jmockit