2012-01-31 5 views
13

Mam klasę Foo, która jest klasą SUT i klasą Bar, która jest jej współpracownikiem. Foo dzwoni run(List<Object> values) na Bar z "expectedList" jako argumentem. Następnie Foo doda kilka dodatkowych elementów do tego List, tak aby jego stan był inny niż był w czasie wywoływania run(). Oto mój przypadek testowy.Czy narzędzie Mockito może weryfikować parametry na podstawie ich wartości w czasie wywołania metody?

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

Zauważ, że współpracownik jest rzeczywiście obiekt szpiegowski zamiast makiety. Ten przypadek testowy zakończy się niepowodzeniem, ponieważ pomimo tego, że run() został wywołany z argumentem równym expectedList, został zmodyfikowany, a jego aktualna wartość nie jest już równa expectedList. Jednak to jest sposób, w jaki powinien działać, więc zastanawiam się, czy istnieje sposób, aby Mockito zapisał migawkę parametrów podczas wywoływania metody i zweryfikował je na podstawie tych wartości, a nie ostatnich wartości.

Odpowiedz

11

Użyj Answer sprawdzić wartość argumentu, gdy wywoływana jest metoda. Możesz albo rzucić AssertionError w obrębie Answer, jeśli wartość jest zła, albo możesz zapisać wartość i zrobić swoje potwierdzenie na końcu.

+0

Tak, David ma rację. Ze względu na sposób, w jaki tworzone jest API Mockito, nie jest możliwa weryfikacja wielu połączeń z tym samym odwołaniem do argumentu. EasyMock może to zrobić, ponieważ ma fazę oczekiwania przed uruchomieniem kodu produkcyjnego. W każdym razie zamiast 'Odpowiedź' używam' ArgurmentCaptor' i napisać jedną lub więcej asercji na ostateczny stan tej listy, tj. Z FEST-Assert 'assertThat (captor.getValue()). Zawiera (" A "," B ") .contains (" T "," U ");' – Brice

+0

@Brice - jak by to działało inaczej niż podejście Michaela Wilesa? –

+0

Nie jest. To tylko inny sposób osiągnięcia celu testu. Ponieważ przez większość czasu nie trzeba sprawdzać pośrednich argumentów, ale tylko niektóre interakcje miały miejsce i wynik końcowy.Chociaż muszę powiedzieć, czy Tom miał określone wymagania, to zgodził się, że to mu nie pomoże, ale w tym przypadku uniknęłbym zmiennego obiektu w moim kodzie produkcyjnym. Wydaje się, że komunikowanie się między dwoma współpracownikami i wiadomościami powinno być zawsze niezmienne. – Brice

0

Nie można wywołać verify() na obiekcie, który nie jest mock. Czy to masz na myśli?

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

Dzięki temu przykładowy kod zawiera błąd i poprawiłem go. To jednak nie jest moje pytanie. Chodzi o możliwość zweryfikowania argumentu na podstawie jego wartości w czasie wywołania metody, a nie najnowszej. –

-1

Dlaczego nie spróbować użyć przechwytywanie argument do nabycia wartości oczekiwanej liście kiedy go uruchomić, a następnie można go porównać.

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

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

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

Jeśli zmieniona lista jest tą samą instancją, argument argument.getValue() zwróci instancję 'expectedList', a nie kopię, więc jest to zasadniczo to samo co on, prawda? – jhericks

+0

@Michael Wiles Dzięki, ale jak wspomniał Jhericks, ArgumentCaptor przechwytuje oryginalną instancję List. –

+0

Przepraszam, Michael, obniżyłem twoją odpowiedź, ponieważ twoje rozwiązanie ma dokładnie taki sam problem jak test OP, jak wyjaśnił jhericks. –

1

The answer of Dawood ibn Kareem pracował dla mnie, ale brakowało mi przykład, też używam Kotlin i Mockito-Kotlin, więc moje rozwiązanie jest tak:

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 będzie reprezentować wartość właściwości zmienny foo.mutable na ostatnim wezwaniem bar.run(foo). Powinno być również możliwe wykonywanie asercji w bloku then {}.

Powiązane problemy