Mocks, stubs i spy w testach jednostkowych opartych na Mockito
Wprowadzenie do mockowania- Mockito Stubs
Podczas pisania testów jednostkowych prawie zawsze istnieje potrzeba interakcji z obiektami w testowanym kodzie. Na pierwszy rzut oka wydaje się, że najłatwiejszym sposobem zarządzania nimi jest po prostu utworzenie nowego rzeczywistego obiektu i zainicjowanie jego wymaganych pól za pomocą konstruktora, buildera lub setterów. Jednak często nie jest możliwe zrobienie tego w prosty sposób, w rozsądnym czasie lub biorąc pod uwagę bezpieczeństwo. Na szczęście w testach jednostkowych istnieją powszechnie znane metody radzenia sobie ze wszystkimi tymi typami obiektów i zostaną one przedstawione w tym artykule.
Klasy przedstawione poniżej zostaną wykorzystane w dalszych przykładach:
Person:
public class Person {
    private String name;
    private String surname;
    private int age;
    public Person(String name, String surname, int age) {
        this.name = name;
        this.surname = surname;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public int getAge() {
        return age;
    }
}PeopleRepository:
public interface PeopleRepository {
    List<Person> getAllPeople();
    List<Person> getByName(String name);
}PersonService:
public class PersonService {
    private PeopleRepository peopleRepository;
    public PersonService(PeopleRepository peopleRepository) {
        this.peopleRepository = peopleRepository;
    }
    List<Person> getAdults() {
        return peopleRepository.getAllPeople().stream()
                .filter(person -> person.getAge() >= 18)
                .collect(Collectors.toList());
    }
    List<Person> findByName(String name) {
        return peopleRepository.getByName(name);
    }
}Stubs
Stub jest obiektem z przykładową implementacją, która naśladuje prawdziwą, ale z minimalną liczbą metod wymaganych do testu. Używamy go, aby uniknąć dostępu do prawdziwych danych. Stuby zawsze zwracają predefiniowane dane wyjściowe, niezależnie od danych wejściowych.
Stub najlepiej jest stosować, gdy:
- nie ma dostępu do metody zwracającej dane
- dostęp do rzeczywistego obiektu może mieć skutki uboczne (takie jak modyfikacja danych w bazie danych)
Stuby najlepiej sprawdzają się w przypadku prostych metod. W przypadku testowania dużych, skomplikowanych obiektów stuby mogą być trudne w utrzymaniu.
Zakładając, że chcemy przetestować klasę PersonService, ale nie chcemy operować na prawdziwym PeopleRepository. W takiej sytuacji utworzenie stuba wydaje się być dobrym pomysłem:
public class PeopleRepositoryStub implements PeopleRepository{
    @Override
    public List<Person> getAllPeople() {
        Person person1 = new Person("John", "Doe", 35);
        Person person2 = new Person("Joseph", "Dendy", 43);
        return List.of(person1, person2);
    }
    @Override
    public List<Person> getByName(String name) {
        return null;
    }
}Przykładowy test:
@Test
void getAllPeople() {
    //given
    PeopleRepository peopleRepositoryStub = new PeopleRepositoryStub();
    PersonService personService = new PersonService(peopleRepositoryStub);
    //when
    List<Person> people = personService.getAdults();
    //then
    assertThat(people.size(), is(2));
}Mocks
Mock jest „pustym” obiektem, który symuluje obiekt rzeczywisty. Nie jest możliwe wywołanie metody mockowanego obiektu, dopóki nie zostanie dokładnie określone, co ta metoda powinna zwrócić. Mocki są bardzo przydatne i popularne, ponieważ zapewniają dużą elastyczność i dodatkowe funkcje (np. możliwość weryfikacji liczby wywołań metod, parametry wywołań).
Przetestujmy przypadek, gdy baza danych nie zwróci żadnych danych:
@Test
void getEmptyPeopleList() {
    //given
    PeopleRepository peopleRepository = mock(PeopleRepository.class);
    PersonService personService = new PersonService(peopleRepository);
    given(peopleRepository.getAllPeople()).willReturn(Collections.emptyList());
    //when
    List<Person> people = personService.getAdults();
    //then
    assertThat(people.size(), is(0));
}Użyliśmy biblioteki Mockito do stworzenia mocka PeopleRepository, przekazaliśmy go jako parametr konstruktora PersonService i wstępnie zdefiniowaliśmy odpowiedź na wywołanie metody getAllPeople. Jak widzimy na przedstawionym przykładzie, użycie mocka jest dość łatwe i elastyczne.
Spies
Spy jest obiektem mieszanym, składającym się z obiektu rzeczywistego i pozorowanego. Zachowuje się jak prawdziwy obiekt, ale zachowanie określonych metod może być mockowane. Spies są przydatni, gdy istnieje klasa z wieloma metodami i istnieje potrzeba mockowania tylko części z nich.
Przykładowy test:
@Test
void getPersonNameAndAge() {
    //given
    Person realPerson = new Person("John", "Doe", 35);
    Person spyPerson = spy(realPerson);
    given(spyPerson.getAge()).willReturn(20);
    //when
    String actualName = spyPerson.getName();
    int actualAge = spyPerson.getAge();
    //then
    assertThat(actualName, is("John"));
    assertThat(actualAge, is(20));
}Jak widzimy, został utworzony prawdziwy obiekt Person i spy oparty na prawdziwym. Metoda GetName nie została zmodyfikowana, więc oczekujemy danych wyjściowych z prawdziwego obiektu, ale jednocześnie oczekujemy zmodyfikowanego wieku. Udało nam się zachować zachowanie jednej prawdziwej metody i zmienić pozostałe.
Podsumowanie
W artykule przedstawiono i omówiono trzy rodzaje podstawowych zaślepek testów jednostkowych. Teraz powinieneś znać ich charakterystykę i różnice między nimi, a także wiedzieć, kiedy używać każdego z nich.
Bibliografia
FAQ
Czym jest Stub w Mockito?
Stub to obiekt z przykładową implementacją, która naśladuje prawdziwą, ale z minimalną liczbą metod wymaganych do testu.
Czym jest Mock w Mockito?
Mock to „pusty” obiekt tworzony za pomocą Mockito, któremu można przypisać zachowania metod i weryfikować ich wywołania. Jest bardziej elastyczny niż stub.
Czym różni się Stub od Mocka w Mockito?
Stub zwraca predefiniowane dane niezależnie od wejścia i najlepiej nadaje się do prostych metod, natomiast Mock w Mockito pozwala elastycznie definiować odpowiedzi i weryfikować wywołania metod.
Poznaj mageek of j‑labs i daj się zadziwić, jak może wyglądać praca z j‑People!
Skontaktuj się z nami


