2011-09-02 65 views
0

我試圖使用Specs2和Mockito測試一些Scala代碼。我對三者都比較陌生,並且在模擬方法返回null時遇到困難。Scala,Specs2,Mockito和null返回值

在下面(有一些名稱的變化轉錄)

"My Component's process(File)" should { 

    "pass file to Parser" in new modules { 
     val file = mock[File] 
     myComponent.process(file) 

     there was one(mockParser).parse(file) 
    } 

    "pass parse result to Translator" in new modules { 
     val file = mock[File] 
     val myType1 = mock[MyType1] 

     mockParser.parse(file) returns (Some(myType1)) 
     myComponent.process(file) 

     there was one(mockTranslator).translate(myType1) 
    } 

    } 

「通行證文件分析器」的作品,直到我加入了SUT譯者呼叫,然後死因爲mockParser.parse方法返回一個空,譯者碼無法使用。

同樣,「將解析結果傳遞給翻譯器」,直到我嘗試在SUT中使用翻譯結果爲止。

這兩種方法的實際代碼不能返回null,但我不知道如何告訴Mockito使期望返回可用結果。

我當然可以通過在SUT中設置空檢查來解決這個問題,但我寧願不要,因爲我確保永遠不會返回空值,而是使用Option,NoneSome

指針,以良好的斯卡拉/ Specs2 /的Mockito教程將是美好的,因爲會如何改變像

there was one(mockParser).parse(file) 

線,使其返回的東西,它允許在SUT時,繼續執行一個簡單的例子它不涉及空值。

揮舞約試圖弄清楚這一點,我試圖改變該行

there was one(mockParser).parse(file) returns myResult 

與myResult的值是我想要返回的類型。這給了我一個編譯錯誤,因爲它期望在那裏找到MatchResult而不是我的返回類型。

如果很重要,我使用的是Scala 2.9.0。

回答

3

如果你沒有看到它,你可以看看specs2文檔的mock expectation page

在你的代碼,存根應mockParser.parse(file) returns myResult

唐的編輯後,編輯:

有一個誤解。你做你的第二個例子的方式是好的,你應該做的第一次測試中如出一轍:

val file = mock[File] 
val myType1 = mock[MyType1] 

mockParser.parse(file) returns (Some(myType1)) 
myComponent.process(file) 
there was one(mockParser).parse(file) 

與模擬單元測試的思想始終是相同的:說明你的嘲弄如何工作(存根),執行,驗證。

這應該回答這個問題,現在個人建議:

大部分的時間,但如果你想驗證某些算法行爲(停止在第一次的成功,過程以相反的順序列表),你不應該測試期望在你的單元測試中。

在你的例子中,process方法應該「翻譯事物」,因此你的單元測試應該關注它:模擬你的解析器和翻譯器,對它們進行存根並且只檢查整個過程的結果。它的粒度不是很好,但單元測試的目標不是檢查方法的每一步。如果你想改變實現,你不應該修改一堆單元測試來驗證每一行的方法。

+0

我在第二個測試的存根使用這個,但問題是在期望而不是存根。我會編輯澄清。 –

+0

我已經完成了編輯,現在我將查看您鏈接的頁面。我想我以前看過,但可能錯過了之前閱讀的一條重要線索。 –

+0

我已經編輯了我的答案 – Nicolas

0

如果沒有完整,很難說,但你可以檢查你試圖模擬的方法不是最終的方法嗎?因爲在那種情況下,Mockito將無法嘲笑它並且會返回null。

另一條建議是,當某些東西不起作用時,就是在標準JUnit測試中用Mockito重寫代碼。那麼,如果它失敗了,你的問題可能最好由Mockito郵件列表中的某個人回答。

+0

這不是最終的。模擬和存根(stub)可以工作,測試通過在被測試的代碼中添加空檢查。 –

1

我已經設法解決了這個問題,雖然可能有更好的解決方案,所以我會發布我自己的答案,但不能立即接受。

我需要做的是爲模擬提供合理的默認返回值,形式爲org.mockito.stubbing.Answer<T>,T是返回類型。

我可以用下面的模擬設置要做到這一點:

val defaultParseResult = new Answer[Option[MyType1]] { 
    def answer(p1: InvocationOnMock): Option[MyType1] = None 
} 
val mockParser = org.mockito.Mockito.mock(implicitly[ClassManifest[Parser]].erasure, 
         defaultParseResult).asInstanceOf[Parser] 

位的來源爲org.specs2.mock.Mockito特點和東西它調用瀏覽後。

現在,不是在返回null時,解析返回時不返回None(包括在第一次測試中預期的時間),這允許測試在測試代碼中使用時使用此值。

我可能會做一個測試支持方法,在mockParser賦值中隱藏混亂,並讓我對各種返回類型做同樣的事情,因爲我需要相同的功能以及幾種返回類型,就在這組試驗。

我找不到支持在org.specs2.mock.Mockito做這個更短的方式,但也許這會激勵埃裏克添加這樣的。很高興有筆者在交談...

編輯

在源的進一步細讀,它發生,我認爲我應該能夠只是調用該方法

def mock[T, A](implicit m: ClassManifest[T], a: org.mockito.stubbing.Answer[A]): T = org.mockito.Mockito.mock(implicitly[ClassManifest[T]].erasure, a).asInstanceOf[T] 

定義在org.specs2.mock.MockitoMocker,這實際上是我上面的解決方案的靈感。但我無法弄清楚這個電話。 mock是相當重載,我所有的嘗試似乎最終調用一個不同的版本,不喜歡我的參數。

所以它看起來像埃裏克已包括支持這一點,但我不明白如何去它。

更新

我已經定義,它包含以下特徵:

def mock[T, A](implicit m: ClassManifest[T], default: A): T = { 
    org.mockito.Mockito.mock(
     implicitly[ClassManifest[T]].erasure, 
     new Answer[A] { 
     def answer(p1: InvocationOnMock): A = default 
    }).asInstanceOf[T] 
    } 

,現在通過使用特質我可以設置我的嘲笑爲

implicit val defaultParseResult = None 
val mockParser = mock[Parser,Option[MyType1]] 

我不畢竟在這個特定的測試中需要更多的用法,因爲提供一個可用的值使得我的所有測試都可以在代碼中沒有null檢查的情況下工作。呃測試。但在其他測試中可能需要它。

我仍然有興趣如何處理這個問題,而不增加這個特點。

+0

在最新的1.7-SNAPSHOT中,我添加了通過任何類型的Mockito設置的可能性,包括默認答案。如果你有更詳細的答案,你可以寫:'val mockParser = mock [Parser] .defaultAnswer((i:InvocationOnMock)=>無論(我))'。該文檔可在[這裏](http://etorreborre.github.com/specs2/guide-SNAPSHOT/org.specs2.guide.Matchers.html#Mocks+creation+and+settings) – Eric

+0

謝謝。我會看看這個。 –