Mockito

Z PrePedia
Skocz do: nawigacja, szukaj
Information icon4.svg W Wikipedii odbyła się dyskusja nad usunięciem tego artykułu, zobacz ją.

Mockito jest otwarto źródłowym frameworkiem testowym dla javy ułatwiającym tworzenie tzw. mock object i nie tylko.

Cel[edytuj]

Mock object jest obiektem który symuluje działanie prawdziwego odpowiednika. Podczas testowania często napotykamy sytuacje, gdzie żeby przetestować wszystkie możliwe scenariusze musimy się sporo napracować, żeby przygotować zaplecze to testów. Utworzyć obiekty, wszystkie zależności i na koniec spiąć to razem. Czasem nawet nie jesteśmy wstanie odtwarzać kontekstu w jakim znalazł się nasz testowany kod bez ingerencji z zewnątrz. Prostym przykładem może być zależność naszego kodu od jakieś konkretnej daty. Proces musi wykonać się cyklicznie co miesiąc. Żeby sprawdzić czy aby na pewno metoda wykona się każdego pierwszego dnia miesiąca o godzinie 00:00:00 musielibyśmy przestawić zegar systemowy.

Cechy[edytuj]

  • mockowanie klas i interfejsów. Jedno api dla obu przpadków.
  • istnieje tylko jeden typ moków, jeden sposób na stworzenia go. Szybko można nauczyć się podstaw korzystania frameworku.
  • refaktoryzacja kodu nie powoduje błędów testów jak w niektórych innych rozwiązaniach.
  • mockito pozwala „szpiegować” obiekty. Możemy weryfikować czy metody zostały wywołane z odpowiednimi parametrami i ile razy.
  • przejrzystość, prostota i mała ilość kodu potrzebna do utworzenia. Obiekt można wykorzystać zaraz po stworzeniu o ile nie zawiera większej logiki. Metody są wywoływane nie powodując błędu, nic nie robią.
  • w przypadkach gdy musimy utworzyć sporo obiektów zależnych lub kod musi się wykonać w odpowiednim środowisku(połączenie z bazą danych), oznacza szybsze wykonanie się samego testu,

Sposób działania[edytuj]

Mockito tworzy w „locie” obiekty które dziedziczą, implementują wskazany obiekt i dzięki temu możemy posługiwać się nim w miejscach gdzie oczekujemy tego prawdziwego. Dodatkowo możemy w miejsce istniejących metod wstawić swoją logikę na potrzeby kodu.

Przykład zastosowania[edytuj]

Mamy klasę, repozytorium, którego celem jest pośredniczenie pomiędzy bazą a innymi obiektami w naszym systemie. Nasza kala zawiera EntityManagera który komunikuje się z bazą poprzez ORM w celu pobrania użytkownika. Metoda którą chcemy przetestować otrzymuje parametr Long którym jest id użytkownika w bazie, a zwrócić ma tylko nazwę użytkownika. Prawda że nic prostszego ? Oprócz tego że żeby sprawdzić czy nasza metoda odpowiednio pobiera nazwę użytkownika i ją zwraca musimy:

  • wystartować bazę(niech nawet będzie w pamięci)
  • załadować konfigurację do mapowania obiektów
  • utworzyć i zapisać naszego użytkownika, żeby później go odczytać.

Jak w takim razie możemy ułatwić sobie pracę ? Tworzymy mocka EntityMenagera który dla danego id zwróci utworzonego przez nas użytkownika bez całej tej otoczki bazodanowej. Oczywiście w tym momencie testujemy tylko samą cześć kodu który odpowiedzialna jest za pobranie nazwy użytkownika. Musimy mieć pewność że pobierania działa więc testy prawności zapisu i odczytu muszą znaleźć się gdzie indziej by aplikacja była w 100% przetestowana. Przykładowy kod poniżej.

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;

import javax.persistence.EntityManager;

import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;


/**
 * Przykładowe repozytorium do zarządzania obiektem użytkownika
 *
 */
class UserRepository {

	EntityManager em;

	public String getUsernameById(Long id) {
		AppUser user = em.find(AppUser.class, id);

		return user.getUsername();
	}

}
/**
 * Klasa zawierająca testy
 *
 */
public class MyTest {

	private UserRepository repositroy = new UserRepository();

	private static final String USER_NAME = "testname";

	private static final Long USER_ID = 1L;

	/**
	 * Metoda pomocnicza przygotowująca obiekty
	 */
	@Before
	public void init() {

		// tworzymy mock entity menagera
		// już teraz możemy posługiwać się naszym obiektem, tylko że każde wywołanie zwróci null
		EntityManager mock = Mockito.mock(EntityManager.class);

		// przgotowujemy użytkownika
		User user = new User();
		user.setUsername(USER_NAME);

		// dopiero teraz ustawiamy co ma zrobić nasz obiekt w momencie wywałkowania danej metody
		Mockito.when(mock.find(User.class, USER_ID))
		        .thenReturn(user);

		// podstawiamy nasz obiekt w repozytorium
		repositroy.em = mock;
	}

	/**
	 * Metoda testująca poprawnie zwróconą nazwę użytkownika
	 */
	@Test
	public void getUserNameByIdTest() {
		// sprawdzenie czy repozytorium zwraca poprawnie nazwę użytkownika
		// Metody testujące z biblioteki Hamcrest
		assertThat(repositroy.getUsernameById(USER_ID), is(USER_NAME));
	}

}

Wady[edytuj]

Jako że nie wszystko na świecie jest idealne to i tu znajdziemy parę problemów.

  • punktu widzenia TDD(Test-driven development) metody prywatne nie istnieją. Nie da się przetestować jednostkowo ani utworzyć mocka z metodą prywatną, Oczywiście można testować całość ale nie w tym rzecz.
  • mockito także nie daje nam możliwości mockowania metod statycznych. Autorzy tłumaczą się że preferują rozwiązania obiektowe i wzorzec wstrzykiwania zależności nad proceduralnym kodem który jest ciężki do zrozumienia i zmian. Implementacja tej funkcjonalności polegałaby na zmianach w bytecode klas a to już nie idzie w parze z ideą testów jednostkowych.
  • nie można mockować klas które są oznaczone jak final
  • nie można mockować metod które są oznaczone jako final

Linki[edytuj]