Test Driven Development (TDD)

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 ?

  1. 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.

  2. 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.

  3. Ł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);
        }