2017-04-25 46 views
1

我有一個簡單的Bar,做一些字符串的操作:驗證的Mockito與列表改變

public interface Bar { 

    void action(List<String> strings); 

} 

然後我Foo這是測試。它隨着時間的推移構建了一個字符串列表,然後最終使用Bar來處理它們。處理完後,隊列被清除。

public class Foo { 

    private final Bar bar; 
    private final List<String> strings; 

    public Foo(Bar bar) { 
    this.bar = bar; 
    this.strings = new ArrayList<>(); 
    } 

    public void addStrings(String... strings) { 
     Collections.addAll(this.strings, strings); 
    } 

    public void processStrings() { 
    bar.action(strings); 
    strings.clear(); 
    } 

} 

我試圖驗證Bar.action被預期的字符串調用。但是,Mockito沒有檢測到真實的調用與實際參數,因爲調用Bar.action後被使用的的列表被清除。

我可能做錯了什麼,但是這對我來說就像是一個臭蟲。這裏的測試:

@Test 
public void shouldVerify() { 
    // Given 
    Bar bar = mock(Bar.class); 
    Foo foo = new Foo(bar); 
    foo.addStrings("Hello", "World"); 
    foo.addStrings("!!!"); 
    // When 
    foo.processStrings(); 
    // Then 
    verify(bar).action(Arrays.asList("Hello", "World", "!!!")); 
} 

比較失敗:

預計:bar.action([你好,世界!]);

Actual:bar.action([]);

可以肯定的是,註釋掉strings.clear()可以讓Mockito正確驗證。


我可以做一個新的列表解決這個問題傳遞給Bar,如:

public void processStrings() { 
    bar.action(new ArrayList<>(strings)); 
    strings.clear(); 
} 

回答

3

不能完全確定在這裏,但是這似乎是的的Mockito限制(但不在那引用我)。起初我以爲這可以通過ArgumentCaptor來解決,但它捕獲相同的實例,並且在您驗證它時已經被修改。

根據this answer你可以使用Answer來驗證方法被調用時的參數。由於Bar::action具有返回類型的void,因此需要稍微不同的方法。 Mockito docs建議使用doX()系列方法。

public void name() { 
    Bar bar = mock(Bar.class); 
    doAnswer(invocation -> { 
     Object[] args = invocation.getArguments(); 
     List<String> list = (List<String>) args[0]; 
     assertThat(list.size(), is(3)); 
     return null; 
    }).when(bar).action(anyList()); 

    Foo foo = new Foo(bar); 
    foo.addStrings("Hello", "World"); 
    foo.addStrings("!!!"); 
    foo.processStrings(); 
} 

doAnswer塊中,您可以在傳遞給模擬時訪問該列表。斷言可以在那裏完成,或者您可以存儲該參數並在稍後執行。我會說,解決方案雖然不是很優雅。