2012-01-31 57 views
13

我有一個Foo類是SUT和Bar類,這是它的合作者。 FooBar上調用run(List<Object> values),將「expectedList」作爲參數。然後,Foo將爲此List添加幾個元素,以使其狀態與調用run()時的狀態不同。這是我的測試案例。Mockito可以在方法調用時根據它們的值驗證參數嗎?

@Test 
public void testFoo() { 
    Bar collaborator = spy(new Bar()); 
    Foo sut = new Foo(collaborator); 
    verify(collaborator).run(expectedList); 
} 

注意,合作者實際上是一個間諜對象,而不是模仿。此測試用例將失敗,因爲即使使用等於expectedList的參數調用run(),它也會被修改,因爲它的當前值不再等於expectedList。然而,這是它應該工作的方式,所以我想知道是否有方法讓Mockito在調用方法時存儲參數的快照,並根據這些值而不是最新的值對其進行驗證。

回答

11

使用Answer檢查方法調用時參數的值。如果值錯誤,您可以將AssertionError丟到Answer之內,或者您可以存儲該值,並在最後進行斷言。

+0

是的,大衛是對的。由於Mockito API的製作方式,不可能使用相同的參數引用來驗證多個調用。 EasyMock可以這樣做,因爲它在生產代碼運行之前有一個期望階段。無論如何,我不是使用'Answer',而是使用'ArgurmentCaptor'並在該列表的最後狀態中寫入一個或多個斷言,即使用FEST-Assert' assertThat(captor.getValue())。contains(「A」,「B 「).contains(」T「,」U「);' – Brice 2012-02-01 13:33:09

+0

@Brice - 這與Michael Wiles的方法有什麼不同? – 2012-02-01 20:28:13

+0

它不是。這只是實現測試目的的一種不同方式。因爲大多數時候你並不需要檢查中間參數,而只是發生了一些相互作用和最終結果。雖然我必須說,如果湯姆有具體的要求,然後同意這不會幫助他,但在這種情況下,我會避免我的生產代碼中的可變對象。感覺兩個協作者和消息之間的消息應該永遠是不可變的。 – Brice 2012-02-02 15:15:16

0

對於不是mock的對象,不能調用verify()。這是你的意思嗎?

Bar collaborator = mock(Bar.class); 
Foo sut = spy(new Foo(collaborator)); 
verify(collaborator).run(expectedList); 
+0

謝謝,示例代碼有一個錯誤,我糾正它。那不是我的問題。這是關於能夠根據方法調用時的值驗證參數,而不是最新的。 – 2012-01-31 23:34:23

-1

爲什麼不嘗試使用參數捕獲獲取期望列表的值,當它運行,然後你可以比較它。

ArgumentCaptor<List> listCaptor = ArgumentCaptor.forClass(List.class); 

verify(collaborator).run(listCaptor.capture()); 

assertEquals(expectedList, argument.getValue()); 
+6

如果你修改過的列表是同一個實例,那麼argument.getValue()將返回'expectedList'實例,而不是副本,所以這和本質上是一樣的,不是嗎? – jhericks 2012-01-31 21:47:40

+0

@Michael Wiles謝謝,但正如jhericks所提到的,ArgumentCaptor捕獲了原始的List實例。 – 2012-01-31 23:31:07

+0

對不起,邁克爾,我低估了你的答案,因爲你的解決方案與OP的測試有完全相同的問題,正如jhericks所解釋的那樣。 – 2012-02-01 00:33:25

1

The answer of Dawood ibn Kareem爲我工作,但我缺少一個例子,也是我用科特林和Mockito-Kotlin,所以我的解決辦法是這樣的:

class Foo(var mutable: String) 

interface Bar { 
    fun run(foo: Foo) 
} 

@Test fun `validate mutable parameter at invocation`() { 
    val bar = mock<Bar>() 

    var valueAtInvocation: String? = null 
    whenever(bar.run(any())).then { 
     val foo = it.arguments.first() as Foo 
     valueAtInvocation = foo.mutable // Store mutable value as it was at the invocation 
     Unit // The answer 
    } 

    val foo = Foo(mutable = "first") 
    bar.run(foo) 
    valueAtInvocation isEqualTo "first" 

    foo.mutable = "second" 
    bar.run(foo) 
    valueAtInvocation isEqualTo "second" 
} 

valueAtInvocation將在最後調用代表可變屬性foo.mutable的價值bar.run(foo)。也應該可以在then {}區塊內做出斷言。

相關問題