Test Driven Development

Test Driven Development to technika tworzenia oprogramowania, która odwraca domyślny porządek tworzenia kodu. Najpierw pisane są testy, a dopiero później implementowana jest nowa funkcjonalność. To podejście polega na wielokrotnym powtarzaniu kroków (tzw. cykl: red – green – refactor):

  1. RED: pierwszym krokiem jest napisanie testu, który ma sprawdzać dodawaną funkcjonalność. Test nie powinien się wykonać pozytywie, ponieważ sama funkcjonalność jeszcze nie jest zaimplementowana.
  2. GREEN: implementacja danej funkcjonalności, przez której brak test się nie wykonał. Dodajemy tylko tyle kodu, ile jest niezbędne do pozytywnego wykonania testu. Na tym etapie test powinien się udać.
  3. REFACTOR: refaktoryzacja napisanego kodu. Na tym etapie nie zmieniamy funkcjonalności, jedynie „oczyszamy” kod, np. wydzielamy metodę, usuwamy powielający się kod.

Po zakończeniu cyklu można przejść do następnej iteracji i zacząć cały cykl od początku. Ważne, aby na bieżąco uruchamiać wszystkie testy, żeby mieć kontrolę na tym, czy zmiany, które utworzyliśmy nie powodują nie przechodzenia pozostałych testów.

Przykład: Tworzymy książkę adresową, gdzie będą zapisywane poszczególne kontakty.

TEST 1. Utworzenie klasy testowej – ContactTest– kontakty do książki adresowej.

RED – utworzenie metody testowej shouldNewContactHasAProperDatas(). Sprawdzenie, czy utworzony kontakt posiada imię, które zostało podane. W tym momencie brak jest klasy Contact oraz metody getName().

class ContactRepositoryTest {

    @Test
    void shouldContactBeAddedToRepository(){
        //given
        ContactRepository contactRepository = new ContactRepository();
        Contact contact = new Contact("Anna", "Nowak", "777888999");

        //when
        contactRepository.addContact(contact);
    }
}

GREEN – utworzenie klasy Contact, konstruktora, metody getName() oraz metody get() dla pozostałych zmiennych. Test się wykonuje poprawnie.

public class Contact {

    private String name;
    private String surname;
    private String telNumber;
    public Contact(String name, String surname, String telNumber) {
        this.name = name;
        this.surname = surname;
        this.telNumber = telNumber;
    }

    public String getName() {
        return name;
    }
    
    public String getSurname(){
        return surname;
    }
    
    public String getTelNumber(){
        return telNumber;
    }
}

REFACTOR – na tym etapie nie potrzeby refaktoryzacji.

TEST 2. Utworzenie klasy testowej ContactRepositoryTest – klasa ContactRepository ma za zadanie przechowywać obiekty klasy Contact – tzw. książka adresowa.

ITERACJA 1:

RED – sprawdzanie będzie, czy kontakty dodają się do listy – shouldContactBeAddedToRepository(). Test się nie wykonuje (brak klasy ContactRepositoryTest). Teraz brakuje klasy ContactRepository oraz metody addContact().

class ContactRepositoryTest {

    @Test
    void shouldContactBeAddedToRepository(){
        //given
        ContactRepository contactRepository = new ContactRepository();
        Contact contact = new Contact("Anna", "Nowak", "777888999");

        //when
        contactRepository.addContact(contact);
    }
}

GREEN – aby kod przeszedł test, musi zostać dodana klasa ContactRepository oraz metoda addContact() do klasy ContactRepository.

public class ContactRepository {

    public void addContact(Contact contact) {
    }
}

REFACTOR – na tym etapie nie potrzeby refaktoryzacji.

ITERACJA 2:

RED – sprawdzenie asercji – czy kontakt został dodany do listy. Test nie przechodzi – brakuje metody getAllContacts() oraz ciała metody addContact().

class ContactRepositoryTest {

    @Test
    void shouldContactBeAddedToRepository(){
        //given
        ContactRepository contactRepository = new ContactRepository();
        Contact contact = new Contact("Anna", "Nowak", "777888999");

        //when
        contactRepository.addContact(contact);
        
        //then 
        assertThat(contactRepository.getAllContacts().get(0), is(contact));
    }
}

GREEN – utworzenie metody getAllContacts() oraz dodanie ciała metody addContact(), żeby asercja była spełniona i test się udał.

public class ContactRepository {

    private List<Contact> contacts = new ArrayList<>();
    public void addContact(Contact contact) {

    }

    public List<Contact> getAllContacts() {
        return contacts;
    }
}

REFACTOR – w założeniu to nie koniec testowania. Praktycznie do każdego testu będziemy potrzebowali instancji klasy ContactRepository, dlatego możemy je zapisać jako pole w klasie testowej.

class ContactRepositoryTest {

    ContactRepository contactRepository = new ContactRepository();
    @Test
    void shouldContactBeAddedToRepository(){
        //given
        Contact contact = new Contact("Anna", "Nowak", "777888999");

        //when
        contactRepository.addContact(contact);

        //then
        assertThat(contactRepository.getAllContacts().get(0), is(contact));
    }
}

Leave a Comment

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *