2011-04-09 58 views

回答

9

你應該可以使用任何模擬庫來模擬協議。在封面之下,每個協議都使用Java接口作爲實現細節,您可以嘲笑該接口。

這就是說,不要這樣做!由於反射,保護級別,最終類別等原因,Java中的嘲諷非常複雜。任何時候如果您想要實現協議的Clojure對象,只需撥打reify(例如,

(defprotocol Foo (method-a [_]) (method-b [_])) 
-> Foo 

(let [stub (reify Foo (method-a [_] :stubbed))] 
    (method-a stub)) 
-> :stubbed 

請注意,您不需要存儲您不打算調用的方法。

+1

這就是存根。怎麼樣嘲笑?你如何定義和驗證期望_idiomatically_? – 2013-03-18 16:53:35

+0

我同意@Dadinn。看起來有一流的支持來陳述關於行爲的斷言,並證實他們發生的事情會隨着st ...而變得渺茫......例如,驗證protocol.f被調用了3次...什麼,我創建一個var,mutate它,然後檢查值?這不是很說明性的。當然有人寫了一些東西,使這需要少一點的樣板。 – 2015-06-03 21:14:31

2

看起來Midje的最新版本很好地提供了這種功能。首先,我想說明的是,當將較大的程序拆分爲組件(例如通過依賴注入與Stuart Sierra的component library等庫一起組裝)時,這種模擬非常有用。如果我有一個隔離一套副作用功能集成到一個概念上的分量我當然希望有一個測試框架的組成部分,這將讓我注入一個獨立的組件,這樣我可以:

  1. 寫一段代碼,在它存在之前使用該組件(自上而下)。
  2. 單獨使用該組件的測試函數與組件的實際實現隔離。

您可以使用Mockito或其他庫,但我同意這樣的解決方案不會特別優雅。

不幸的是,協議和記錄產生Midje不能侵入一樣容易,功能類......這樣你就需要稍微修改代碼:

(defrecord-openly SideEffectThing [connection] 
ISideEffect 
(persist [this thing] :unfinished) 
(read [this] :unfinished) 
) 

有關如何進行此修改的詳細信息,請參閱Midje's documentation on production mode不會影響您的生產運行時。

(fact "you can test in terms of a record's methods" 
    (let [obj (->SideEffectThing :fake-connection)] 
    (user-of-side-effect-thing obj) => 0 
    (provided 
    (read obj) => -1) 
) 
) 

你可以,當然,避免依賴於生產類型在這裏:

通過與defrecord-openly定義您的組件,您可以使用Midje的「規定」機制獲得指定的組件的方法的行爲的能力(我會主張),並且還要避免在整個生產代碼中公開使用defrecord。在這種情況下,只需將SideEffectThing(如上所述)移動到您的測試代碼中即可。然後,應用程序中的組件系統可以插入實際組件,但是可以針對未實現的測試版本編寫測試。

爲了完整起見,我會將等效的Java Mockito代碼與上面的解決方案進行比較。在Java:

interface ISideEffect { int read(); void write(Object something); } 
class SideEffectThing implements ISideEffect { ... } 

// in test sources: 
public class SomeOtherComponentSpec { 
    private ISideEffect obj; 
    @Before 
    public void setup() { obj = mock(ISideEffect.class); } 
    @Test 
    public void does_something_useful() { 
     when(obj.read()).thenReturn(-1); 
     OtherComponent comp = new OtherComponent(obj); 

     int actual = comp.doSomethingUseful(); 

     assertEquals(0, actual); 
     verify(obj).read(); 
    } 

此Java解決方案嘲笑了組件,指定組件所需的行爲,那麼不僅會檢查該組件本身可正常工作,而且該組件依賴於調用read()某種程度上來說。Mockito還支持參數(和捕獲)的模式匹配,以分析組件是如何使用的,如果需要的話。

上面的Midje示例做了很多工作,並且形式更清晰。如果您在(在提供的子句中)指出該函數是用特定的參數調用的,那麼測試將會失敗,如果不是。如果你指定該函數被多次調用(而不是),那麼這是一個失敗。例如,要指示讀會被稱爲3倍,而應返回不同的值:

(fact "you can test in terms of a record's methods" 
    (let [obj (->SideEffectThing :fake-connection)] 
    (user-of-side-effect-thing obj) => 0 
    (provided 
    (read obj) => -1 
    (read obj) => 6 
    (read obj) => 99 
    ) 
) 
) 

表明您希望閱讀被稱爲三次,它應該返回值的指定序列。有關更多詳細信息,請參閱docs on prerequisites,其中包括如何在提供的單個函數規範中指定確切的次數,以及如何指示該函數從不被調用。