Test jednostkowy polega na tym, że sprawdzasz, odizolowaną od reszty zależności, daną klasę czy metodę. Chodzi o to że testujesz czy dana klasa robi to do czego została stworzona. Jeśli klasa posiada jakiś dodatkowy element (tzn. jest od czegoś zależna) czyli np. pobiera dane z bazy, to do testu jednostkowego, trzeba zrobić testową bazę, która ma przygotowane jakieś dane i na tych testowych danych robić test. Innym wyjściem jest zrobienie tzw. mocka czyli coś co powoduje, że dana zależność np. baza danych jest ukrywana i zastępowana przez sztuczny obiekt, którym zarządza programista. Do tego przydaje się np. framework Mockito, który ułatwia zastępowanie zależności sztucznymi obiektami. Jeśli klasa nie ma żadnych zależności to jest to sytuacja piękna i klarowna, testy piszę się dokładnie tak prosto jak do Twojego kalkulatora. Jednak duży projekt może nie mieć takich klas i trzeba zaprzęgać właśnie np. mockito. W mockowaniu przede wszystkim chodzi o to, że zapewniasz że obiekt który jest zależnością na pewno działa na 100% i zwraca poprawne dane (poprawne w kontekście danego testu), ponieważ sprawdzasz tylko jedną klasę/funkcjonalność i nie obchodzi cię działanie innej części systemu. W teście jednostkowym trzeba tak ustawiać zależności aby generowały odpowiednie do testu dane, czyli w zależności od tego pod jakim kątem sprawdzasz daną funkcjonalność. Bo możesz sprawdzić coś w sytuacji kiedy wszystko poszło dobrze lub testować czy funkcjonalność zachowuje się odpowiednio kiedy coś poszło nie tak. Na przykład funkcjonalność kupowania - produkt można kupić kiedy jest w magazynie, ale co się stanie kiedy go nie będzie na magazynie? Trzeba zrobić dwa testy. Jeden, który zakłada że towar jest i kupno jest możliwe, Drugi, kiedy towaru nie ma na stanie i sprawdza się czy zostanie np. zgłoszony jakiś komunikat o błędzie:
Mając np. taki kod projektu:
class Product
{
}
interface ProductDao
{
Product getProductById(Long id);
}
public class BuyingService
{
ProductDao productDao; //zależność, kalsa BuyingService zależy od obiektu productDao.
public BuyingService(ProductDao productDao)
{
this.productDao = productDao;
}
public boolean buy(Long idProduct)
{
Product product = productDao.getProductById(idProduct);
if (product != null)
return true;
return false;
}
}
Test może wyglądać np. tak (wykorzystany jest mockito i JUnit):
public class BuyingServiceTest
{
@Test
public void whenProductIsInWarehouseThenBuyingIsPossible()
{
ProductDao productDaoMock = mock(ProductDao.class); //sztuczny obiekt bazy danych
Product productMock = new Product();
when( productDaoMock.getProductById( anyLong() ) )
.thenReturn(productMock); //ustawiamy ze sztuczny obiekt ma zwracac jakiś produkt
BuyingService buyingService = new BuyingService(productDaoMock); //przekazujemy sztuczny obiekt do serwisu aby zrobic test jednostkowy
boolean actual = buyingService.buy(1L); //uruchamiamy metodę serwisu
assertTrue(actual); //sprawdzamy czy zwrociła to co oczekujemy
}
@Test
public void whenProductIsNotInWarehouseThenBuyingIsImpossible()
{
ProductDao productDaoMock = mock(ProductDao.class); //sztuczny obiekt bazy danych
when( productDaoMock.getProductById( anyLong() ) )
.thenReturn(null); //ustawiamy ze sztuczny obiekt ma zwracac null tak jakby nie było go w bazie
BuyingService buyingService = new BuyingService(productDaoMock); //przekazujemy sztuczny obiekt do serwisu aby zrobic test jednostkowy
boolean actual = buyingService.buy(1L); //uruchamiamy metodę serwisu
assertFalse(actual); //sprawdzamy czy zwrociła to co oczekujemy
}
}
Najlepszym sposobem na zrozumienie testów jest ich pisanie - poczytaj na początek jak korzystać z JUnit i mockito.