Czy Mockito może zastosować metodę bez względu na argument?

302

Próbuję przetestować jakiś starszy kod, używając Mockito.

Chcę wkleić plik FooDaoużywany w produkcji w następujący sposób:

foo = fooDao.getBar(new Bazoo());

Umiem pisać:

when(fooDao.getBar(new Bazoo())).thenReturn(myFoo);

Ale oczywistym problemem jest to, że getBar()nigdy nie jest wywoływany z tym samym Bazooobiektem, dla którego zastosowałem metodę. (Przeklnij tego newoperatora!)

Bardzo by mi się podobało, gdybym mógł zlikwidować metodę w taki sposób, że zwraca ona myFooniezależnie od argumentu. W przeciwnym razie wysłucham innych sugestii dotyczących obejścia tego problemu, ale naprawdę chciałbym uniknąć zmiany kodu produkcyjnego, dopóki nie będzie rozsądnego zasięgu testu.

Eric Wilson
źródło

Odpowiedzi:

456
when(
  fooDao.getBar(
    any(Bazoo.class)
  )
).thenReturn(myFoo);

lub (aby uniknąć null):

when(
  fooDao.getBar(
    (Bazoo)notNull()
  )
).thenReturn(myFoo);

Nie zapomnij zaimportować dopasowań (dostępnych jest wiele innych):

Dla Mockito 2.1.0 i nowszych:

import static org.mockito.ArgumentMatchers.*;

W przypadku starszych wersji:

import static org.mockito.Matchers.*;
Tomasz Nurkiewicz
źródło
2
Uwielbiam to, gdy odpowiedź poprzedza koniec „akceptuj zamrożenie odpowiedzi”.
Eric Wilson,
10
Jest notNull(Bazoo.class)taki jak any(Bazoo.class)(być może nie istniał w czasie tej odpowiedzi)
Dandre Allison
2
Miałem nieco szczególną sytuację, w której mogłem mieć jeden z dwóch możliwych argumentów - Bazoolub Cazooktóre są, powiedzmy, podklasami Azoo. bo Bazoomusiałem wrócić foo, ale Cazoomusiałem wrócić bar. w tej sytuacji proponowane Matchers.any()rozwiązanie nie działa, jednak Matchers.isA()działa idealnie.
Tanvir
3
org.mockito.Matchersjest teraz przestarzałe - użyj org.mockito.ArgumentMatcherszamiast tego, tj. import static org.mockito.ArgumentMatchers.*(patrz dokumenty )
DontDivideByZero
when(myFoo.knowsWhatsUp()).thenReturn(myMoney);
6rchid
18

Użyj w ten sposób:

when(
  fooDao.getBar(
    Matchers.<Bazoo>any()
  )
).thenReturn(myFoo);

Zanim będziesz musiał zaimportować Mockito.Matchers

Hamad
źródło
1
To jest deprecjonowane!
DrB
15

http://site.mockito.org/mockito/docs/1.10.19/org/mockito/Matchers.html

anyObject() powinien pasować do twoich potrzeb.

Ponadto zawsze możesz rozważyć wdrożenie hashCode()i equals()dla Bazooklasy. To sprawi, że twój przykład kodu będzie działał tak, jak chcesz.

Buhb
źródło
Zgadzam się z drugą sugestią, ale nadal decyduję się tego nie robić z powodów nietechnicznych.
Eric Wilson,
1
Klasa Matchers jest przestarzała (patrz dokumenty - „Ta klasa prawdopodobnie zostanie usunięta w wersji 3.0” )
Johannes Rabauer,
1

Inną opcją jest poleganie na dobrej starej equalsmetodzie mody . Dopóki argument w whenmakiecie equalsjest argumentem w testowanym kodzie, Mockito będzie pasował do makiety.

Oto przykład.

public class MyPojo {

    public MyPojo( String someField ) {
        this.someField = someField;
    }

    private String someField;

    @Override
    public boolean equals( Object o ) {
        if ( this == o ) return true;
        if ( o == null || getClass() != o.getClass() ) return false;
        MyPojo myPojo = ( MyPojo ) o;
        return someField.equals( myPojo.someField );
    }

}

zakładając, że wiesz, jaka będzie wartość someField, możesz wyśmiewać to w ten sposób.

when(fooDao.getBar(new MyPojo(expectedSomeField))).thenReturn(myFoo);

plusy: jest to bardziej wyraźne niż anydobijacze. Jako recenzent kodu, mam oko na anykod, który piszą młodsi programiści, gdy przegląda logikę swojego kodu, aby wygenerować odpowiedni przekazywany obiekt.

con: Czasami pole przekazywane do obiektu jest losowym identyfikatorem. W tym przypadku nie można łatwo zbudować oczekiwanego obiektu argumentu w kodzie próbnym.

Innym możliwym podejściem jest użycie Answerobiektu Mockito, którego można użyć z tą whenmetodą. Answerpozwala przechwycić rzeczywiste wywołanie, sprawdzić argument wejściowy i zwrócić próbny obiekt. W poniższym przykładzie używam anydo przechwycenia dowolnego żądania wyśmiewanej metody. Ale w Answerlambda mogę dalej sprawdzić argument Bazo ... może w celu zweryfikowania, czy przekazano mu odpowiedni identyfikator. Wolę to nad anysobą, aby przynajmniej przeprowadzić kontrolę argumentu.

    Bar mockBar = //generate mock Bar.

    when(fooDao.getBar(any(Bazo.class))
    .thenAnswer(  ( InvocationOnMock invocationOnMock) -> {
        Bazo actualBazo = invocationOnMock.getArgument( 0 );

        //inspect the actualBazo here and thrw exception if it does not meet your testing requirements.
        return mockBar;
    } );

Podsumowując, lubię polegać na equals(gdzie oczekiwany argument i rzeczywisty argument powinny być sobie równe) i jeśli równość nie jest możliwa (z powodu niemożności przewidzenia rzeczywistego stanu argumentu), ucieknę się do Answerwglądu do argumentu.

Jose Martinez
źródło