Test Driven Development (TDD) to podejście do tworzenia oprogramowania, w którym pisanie testów jednostkowych poprzedza implementację kodu źródłowego. Proces ten skupia się na cyklicznym powtarzaniu trzech etapów: napisanie testu jednostkowego, napisanie kodu, który spełni warunki testu, oraz refaktoryzacja kodu w celu poprawy jego jakości. TDD wprowadza dyscyplinę i skupia uwagę na funkcjonalnościach programu, co przyczynia się do zwiększenia jakości kodu i ułatwia utrzymanie aplikacji.
Jakie są zalety TDD ?
-
Poprawa Jakości Kodu: Dzięki TDD, kod jest bardziej zgodny z wymaganiami, co przekłada się na lepszą jakość oprogramowania. Testy jednostkowe są nieustannie uruchamiane,
co pomaga w szybkim wykrywaniu błędów i utrzymaniu aplikacji w dobrym stanie. -
Szybsze i łatwiejsze debugowanie: TDD pozwala na szybkie wykrywanie i naprawianie błędów, ponieważ każda zmiana w kodzie wymaga aktualizacji testów jednostkowych.
To sprawia, że deweloperzy są świadomi błędów od razu po ich wprowadzeniu. -
Łatwiejsze Rozszerzanie Funkcjonalności: Testy jednostkowe działają jako dokumentacja kodu, co ułatwia zrozumienie, jak korzystać z poszczególnych części systemu. Dodawanie nowych funkcji czy modyfikacja istniejących staje się bardziej kontrolowane i przewidywalne.
Przy pisaniu testów jednostkowych dobrze jest stosować Stuby, Mocki oraz Spy, które
pomagają w kontrolowaniu i izolowaniu poprzez wyeliminowanie zależności od zewnętrznych serwisów czy systemów.
Klasa do testowania
public class Library {
private BookRepository bookRepository;
public Library(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
public boolean borrowBook(Book book) {
if (bookRepository.isBookAvailable(book)) {
bookRepository.updateBookStatus(book, BookStatus.BORROWED);
return true;
}
return false;
}Testy jednostkowe z wykorzystaniem Stuba, Mocka i Spy
- Stub umożliwia symulowanie zachowania metod, co jest przydatne w przypadku testowania jednostkowego, gdy chcemy skoncentrować się na jednej funkcji, pomijając inne zależności.
public class LibraryTest {
@Test
public void testBorrowBookWithStub() {
//given
BookRepositoryStub bookRepositoryStub = new BookRepositoryStub();
Library library = new Library(bookRepositoryStub);
// when
List<Book> bookList = getAllAvailableBooks();
//when
assertTrue(bookList, hasSize(2));
}
}
public class BookRepositoryStub {
public List<Book> gellAllBooks() {
Book book1 = new Book("Makbet", "Willima Shekspir", BookStatus.BORROWED);
Book book2 = new Book("Coma", "Robin Cook", Status.AVAILABLE);
Book book3 = new Book("Szklany Tron", "Sarah J. Maas", Status.AVAILABLED);
return Arrays.as(book1, book2, book3);
}
public Bolean isAvailable(Book book) {
return false;
}
}- Mock dodatkowo pozwala na weryfikację, czy dane metody zostały wywołane z odpowiednimi parametrami, co pomaga w upewnieniu się, że testowana jednostka działa zgodnie z oczekiwaniami
@Test
public void testBorrowBookWithMock() {
//given
BookRepository bookRepositoryMock = mock(BookRepository.class);
Library library = new Library(bookRepositoryMock);
when(bookRepositoryMock.isBookAvailable(any())).thenReturn(true);
doNothing().when(bookRepositoryMock).updateStatus(any(), any());
//when
Book book = new Book("To Kill a Mockingbird", "Harper Lee");
//then
assertTrue(library.borrow(book));
verify(bookRepositoryMock, times(1)).isBookAvailable(book);
verify(bookRepositoryMock, times(1)).updateStatus(book, Status.BORROWED);
}- Spy pozwala śledzić, czy konkretne metody zostały
wywołane i ile razy, co jest przydatne do monitorowania i analizy
zachowań testowanych obiektów
@Test
public void testBorrowBookWithSpy() {
//given
BookRepository bookRepositorySpy = spy(BookRepository.class);
doReturn(true).when(bookRepositorySpy).isBookAvailable(any());
doNothing().when(bookRepositorySpy).updateStatus(any(), any());
Library library = new Library(bookRepositorySpy);
//when
Book book = new Book("1984", "George Orwell");
//then
assertTrue(library.borrowBook(book));
verify(bookRepositorySpy, times(1)).isBookAvailable(book);
}