2010-05-19 37 views
41

思考這個問題的一種方式是:如果我們關心代碼的設計,那麼EasyMock就是更好的選擇,因爲它通過其期望的概念向您提供反饋。EasyMock vs Mockito:設計與可維護性?

如果我們關心測試的可維護性(易於閱讀,編寫,並且不會受到更改影響較小的脆性測試),那麼Mockito似乎是更好的選擇。

我的問題是:

  • 如果您在大型項目中使用EasyMock的,你發現你的測試是難以維持?
  • Mockito(Endo測試除外)的侷限性是什麼?

回答

26

我是一個EasyMock開發者,所以有點偏,但是我已經在大型項目中使用了EasyMock。

我的觀點是EasyMock測試確實會在短時間內破壞一次。 EasyMock強迫你做一個你期望的完整記錄。這需要一些紀律。你應該記錄什麼是預期的,而不是測試方法目前需要什麼。例如,如果一個方法在模擬上調用了多少時間並不重要,那麼不要害怕使用andStubReturn。另外,如果您不關心參數,請使用anyObject()等。 TDD的思考可以幫助解決這個問題。

我的分析是,EasyMock測試會更頻繁地中斷,但Mockito的測試不會在你想要的時候出現。我更喜歡我的測試打破。至少我知道我的發展有什麼影響。這當然是我個人的觀點。

+1

是的,我一直在思考同一個:用的Mockito(和Unitils模擬,類似的嘲諷API)是編寫繼續愉快地時,他們不應該通過試驗容易得多。我懷疑這可能是爲什麼類似Mockito的應用程序接口(API)過度「鬆散」測試的主要原因,通常被認爲「更容易」。 – 2010-06-08 18:06:54

+3

我有興趣看到一個對比這兩種方法的例子... – Armand 2011-01-28 15:23:53

7

我不認爲你應該太在意這件事。 Easymock和Mockito都可以配置爲'strict'或'nice',唯一的區別是默認情況下Easymock是嚴格的,Mockito很好。

由於所有測試都沒有硬性規定,所以您需要平衡測試可信度與可維護性。我通常發現某些功能或技術領域需要高度的信心,我會使用「嚴格」模擬。例如,我們可能不希望debitAccount()方法被多次調用!然而,在其他情況下,模擬實際上只是一個存根,所以我們可以測試代碼的真正「肉」。

在Mockito的生活早期,API兼容性是一個問題,但現在更多的工具支持該框架。 Powermock(我個人的最愛),現在擁有的Mockito擴展

42

如果我們關心代碼的設計則EasyMock的是更好的選擇,因爲它通過它的預期

有趣的概念給出了反饋給您。我發現「期望概念」使得許多開發人員在測試中提出了更多的期望,只是爲了滿足意外的方法調用問題。它如何影響設計?

當您更改代碼時,測試不應該中斷。功能停止工作時,測試應該中斷。如果有人喜歡在任何代碼更改發生時中斷測試,我建議編寫一個測試,聲明java文件的md5校驗和:)

94

我不會爭論這些框架的測試可讀性,大小或測試技術,我相信他們是平等的,但在一個簡單的例子中,我會告訴你不同之處。

考慮:我們有一類是負責地方存放東西:

public class Service { 

    public static final String PATH = "path"; 
    public static final String NAME = "name"; 
    public static final String CONTENT = "content"; 
    private FileDao dao; 

    public void doSomething() { 
     dao.store(PATH, NAME, IOUtils.toInputStream(CONTENT)); 
    } 

    public void setDao(FileDao dao) { 
     this.dao = dao; 
    } 
} 

,我們想測試一下:

的Mockito:

public class ServiceMockitoTest { 

    private Service service; 

    @Mock 
    private FileDao dao; 

    @Before 
    public void setUp() { 
     MockitoAnnotations.initMocks(this); 
     service = new Service(); 
     service.setDao(dao); 
    } 

    @Test 
    public void testDoSomething() throws Exception { 
     // given 
     // when 
     service.doSomething(); 
     // then 
     ArgumentCaptor<InputStream> captor = ArgumentCaptor.forClass(InputStream.class); 
     Mockito.verify(dao, times(1)).store(eq(Service.PATH), eq(Service.NAME), captor.capture()); 
     assertThat(Service.CONTENT, is(IOUtils.toString(captor.getValue()))); 
    } 
} 

了EasyMock:

public class ServiceEasyMockTest { 
    private Service service; 
    private FileDao dao; 

    @Before 
    public void setUp() { 
     dao = EasyMock.createNiceMock(FileDao.class); 
     service = new Service(); 
     service.setDao(dao); 
    } 

    @Test 
    public void testDoSomething() throws Exception { 
     // given 
     Capture<InputStream> captured = new Capture<InputStream>(); 
     dao.store(eq(Service.PATH), eq(Service.NAME), capture(captured)); 
     replay(dao); 
     // when 
     service.doSomething(); 
     // then 
     assertThat(Service.CONTENT, is(IOUtils.toString(captured.getValue()))); 
     verify(dao); 
    } 
} 

你可以看到兩個測試都是相同的,並且兩個測試都通過了。 現在,讓我們假設其他人更改了服務實現並嘗試運行測試。

新服務實現:

dao.store(PATH + separator, NAME, IOUtils.toInputStream(CONTENT)); 

分離器在路徑的終點不斷

加入如何測試結果會像現在呢?首先這兩個測試會失敗,但使用不同的錯誤信息:

了EasyMock:

java.lang.AssertionError: Nothing captured yet 
    at org.easymock.Capture.getValue(Capture.java:78) 
    at ServiceEasyMockTest.testDoSomething(ServiceEasyMockTest.java:36) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 

的Mockito:

Argument(s) are different! Wanted: 
dao.store(
    "path", 
    "name", 
    <Capturing argument> 
); 
-> at ServiceMockitoTest.testDoSomething(ServiceMockitoTest.java:34) 
Actual invocation has different arguments: 
dao.store(
    "path\", 
    "name", 
    [email protected] 
); 
-> at Service.doSomething(Service.java:13) 

在EasyMock的測試發生了什麼事,爲什麼結果還是沒捕捉到?商店方法沒有被執行,但是等一下,那是爲什麼EasyMock對我們說謊?

這是因爲EasyMock在一行中混合兩個職責 - 存根和驗證。這就是爲什麼當出現問題時很難理解哪個部分導致失敗。

當然,您可以告訴我 - 只需更改測試並在斷言之前移動驗證即可。哇,你是否認真,開發者應該記住一些嘲諷框架強制的魔法命令?

順便說一句,它不會幫助:

java.lang.AssertionError: 
    Expectation failure on verify: 
    store("path", "name", capture(Nothing captured yet)): expected: 1, actual: 0 
    at org.easymock.internal.MocksControl.verify(MocksControl.java:111) 
    at org.easymock.classextension.EasyMock.verify(EasyMock.java:211) 

不過,這是對我說沒有執行的方法,但它是,只能用另一個參數。

爲什麼Mockito更好?這個框架不會將兩個職責混合在一個地方,當你的測試失敗時,你會很容易理解爲什麼。

+0

我首先嚐試mockito賣出。測試可維護性已經足夠混亂,而模擬框架無法應對。 – Gary 2012-10-26 03:10:41

+0

我意識到這是舊的......但是,你在這裏混合驗證和存根過程而不是EasyMock。在驗證之前做出斷言是一種糟糕的做法,因此您可以實際刪除'assertThat'調用,因爲驗證會爲您選擇:「FileStore.dao(」path「,」name「,capture(Nothing Captured yet)):預期1,實際:0「。 我會承認Mockito的信息在這裏更加清晰,但在這個太具體的例子中,這並不足以影響某人對EasyMock的決定! – 2015-12-16 12:34:51

+0

我不知道我是如何在EasyMock中混合一些東西的,這是它的工作方式,所以如果你有一個很好的反例,我會很高興看到它。我認爲這是一個個人觀點,不管這個例子是否足夠影響這些框架的決策,我很高興很多人認爲它是有價值的。 – 2015-12-16 22:38:38

5

我更喜歡mockito說實話。一直在使用EasyMock和單元組合,兩者的組合通常會導致異常,如IllegalArgumentException:不是接口以及MissingBehaviorExceptions。在這兩種情況下,儘管代碼和測試代碼都非常好。看起來MissingBehaviorException是由於使用createMock創建的模擬對象(使用classextentions !!)確實產生了這個錯誤。當使用@Mock它確實工作!我不喜歡那種誤導性的行爲,對我而言,這清楚地表明它的開發者不知道他們在做什麼。一個好的框架應該總是易於使用並且不含糊。 IllegalArgumentException也是由於EasyMock內部的混合。另外,錄音不是我想要做的。我想測試我的代碼是否會拋出異常,並返回預期的結果。這與代碼覆蓋率相結合對我來說是正確的工具。我不想讓我的測試每當我把一行代碼放在上一行或下一行時就中斷,因爲這會提高性能等等。隨着mockito這是沒有問題的。使用EasyMock,即使代碼沒有被破壞,也會導致測試失敗。那很不好。它花費時間,因此花錢。你想測試預期的行爲。你真的關心事物的順序嗎?我想在極少數情況下你可能會。然後使用Easymock。在其他情況下,我認爲使用mockito編寫測試的時間會少得多。

親切的問候 勞倫斯