Moduł 1: Zadanie 1 – rozwiązanie

Forum

Zadanie 1

  • Dodaj implementację poniższego interfejsu, bazując na wybranej, standardowej kolekcji np. HashMap
  • Wstrzyknij stworzone repozytorium, jako kolejną zależność FakePaymentService i wykorzystaj ją do zapisywania płatności
  • Spróbuj dodać testy jednostkowe
public interface PaymentRepository {

    Payment save(Payment payment);

}

Zadanie 1 – rozwiązanie

00:08

Spróbujmy teraz zrealizować ćwiczenie przygotowane do modułu pierwszego. Na początek dodajmy interfejs PaymentRepository. W ramach interfejsu zdefiniujemy teraz metodę save. Spróbujemy teraz dodać nowy komponent implementujący interfejs PaymentRepository i opisujący płatności przy użyciu zwykłej hashmapy. Po stworzeniu klasy możemy dodać mapę, która będzie przechowywała nasze płatności. Warto pozwolić na to, żeby ta mapa mogła być również ustawiana z zewnątrz, żeby mogła być konfigurowana. To pozwoli nam na łatwiejsze testowanie naszego repozytorium. Dodamy w związku z tym adnotację @Setter i na koniec pozostaje jeszcze wstawić taką płatność do mapy i zwrócić jako rezultat metody. Żeby płatności mogły być zapisywane, żebyśmy mogli wykorzystać ten świeżo zaimplementowany kod, musimy doprowadzić do tego że nasze repozytorium będzie kolejnym komponentem zarządzanym przez Springa. W związku z tym dodajemy adnotację @Component. Kolejnym krokiem będzie dostarczenie naszego repozytorium na poziomie FakePaymentService. W związku z tym deklarujemy kolejne pole instancyjne. Zwróćmy uwagę na typ tego pola. Jest to interfejs, co pozostawia nam furtkę na podmianę tej implementacji w przyszłości. W momencie kiedy będziemy np. omawiali dostęp do relacyjnych baz danych, spróbujemy w to miejsce podstawić zupełnie inną implementację, która będzie pozwalała na to, żeby utrwalać dane dotyczące płatności w bazie SQL. Dysponując repozytorium, możemy wykorzystać je do tego, żeby zapisać naszą płatność. Ponieważ zmieniliśmy konstruktor na poziomie FakePaymentService musimy naprawić test który został przez nas wcześniej stworzony. Wystarczy stworzyć kolejny obiekt zastępczy i podać do konstruktora FakePaymentService. Nigdy w takim wypadku nie podajemy oryginalnych obiektów, to zawsze powinny być obiekty, które zostały albo stworzone właśnie przy udziale mechanizmu mockujacego czy też jakieś takie proste implementacje, stuby, które pozwalają na to, żeby odizolować testowaną klasę od pozostałych zależności. Kolejnym krokiem będzie zaprogramowanie naszego mocka w taki sposób, żeby pozwalał on na wykonanie metody save. Możemy zatem powiedzieć, że jeżeli ktoś zawoła metodę save i przekaże do niej jakąkolwiek instancję typu Payment wtedy chcielibyśmy zwrócić dokładnie ten sam Payment który został przekazany jako pierwszy argument. Spróbujmy uruchomić testy z poziomu tej klasy. Zobaczmy czy wszystko działa. Spróbujmy również uruchomić aplikację, żeby zweryfikować, czy na tym poziomie nie ma błędów. Udało się. Widać, że wyniki wyglądają na prawidłowe. Natomiast na pewno brakuje nam jeszcze testu, który będzie związany z samym zapisywaniem, będzie potwierdzał to, że taka tworzona płatność faktycznie jest zapisywana do naszego repozytorium. Nie będziemy badali czy ona faktycznie jest zapisywana w hashmapie, bo z punktu widzenia takiej usługi jest coś, co jest szczegółem implementacyjnym i co nie ma żadnego znaczenia. Będziemy natomiast badali czy w ramach tej metody process takie żądanie zapisu następuje. W związku z tym dodamy jeszcze jedną metodę testową. W ramach takiej metody testowej możemy poprosić mocka o informację zwrotną czy została na nim wywołana metoda save i czy przekazano do niej obiekt typu Payment. Ten obiekt typu Payment my już mamy, także będzie można dodatkowo zweryfikować czy to jest ten konkretny Payment, który został utworzony w ramach procesowania żądania płatności. Na poziomie typu Mockito mamy metodę verify. Możemy wskazać mocka który będzie odpowiadał na nasze pytania, który będzie dokonywał weryfikacji. Pytamy go, czy wywołana była metoda save, czy przekazany do tej metody save został Payment. Teraz spróbujemy sobie uruchomić jeszcze raz nasze testy. Wszystkie testy przechodzą także mamy potwierdzenie tego, że ta płatność faktycznie została przekazana do zapisu. Natomiast odpowiedzenie na pytanie, czy implementacja naszego repozytorium oparta o zwykłą hashmapę działa poprawnie wymaga stworzenia osobnej klasy testowej. Czyli powinniśmy stworzyć coś takiego jak HashMapPaymentRepositoryTest i zweryfikować na poziomie takiej klasy, w ramach testów, czy następuje prawidłowy, już taki techniczny zapis, w tym wypadku do hashmapy. Potrzebujemy na pewno znowu testowego identyfikatora. Potrzebujemy także płatności, którą będziemy zapisywali. Tak jak w przypadku poprzednich testów, spróbujemy też zdefiniować wszystkie zależności do naszego testowanego obiektu. W tym wypadku to będzie nasza mapa. To będzie ta kolekcja, która będzie przechowywała, poszczególne płatności. Oczywiście musimy też zdefiniować testowany obiekt. W ramach metody setUp, spróbujemy skonfigurować PaymentRepository żeby wykorzystywało naszą zamockowaną mapę. Pozostaje nam już teraz napisać właściwy test. Sprawdzimy w tym teście czy taka płatność zostanie dodana do naszej hashmapy pod odpowiednim kluczem. Próbujemy teraz zapisać płatność w naszym repozytorium. I weryfikujemy czy faktycznie wykonana została metoda put na poziomie mapy. Oczywiście nasza mapa to mock, to dubler w związku z tym będziemy mieli potwierdzenie czy faktycznie zostało to zrobione poprawnie. Test przechodzi pozytywnie także mamy komplet. Możemy jeszcze spróbować uruchomić wszystkie testy z poziomu Mavena dla całej aplikacji, żeby ostatecznie zweryfikować czy niczego nie zepsuliśmy. Wszystkie dziewięć testów przechodzi pozytywnie. W związku z tym możemy uznać, że jest bardzo duża szansa na to, że nasza aplikacja działa poprawnie. To, że mogliśmy w tak łatwy sposób dodać testy to, że możemy teraz wymieniać implementację, czy to repozytorium, czy generatora identyfikatorów płatności, czy też samą usługę dotyczącą płatności wynika z tego, że zastosowaliśmy trzy bardzo ważne, podstawowe zasady. To znaczy, używaliśmy interfejsów do tego, żeby odseparowywać implementację od kodu, który z tej implementacji korzysta. Z drugiej strony wszystkie zależności podawaliśmy z zewnątrz, konfigurowaliśmy i podawaliśmy z zewnątrz. Z trzeciej strony używaliśmy też programowania aspektowego do tego, żeby oddzielić kod typowo biznesowy od logiki pobocznej. To wprowadzenie i wyjaśnienie tych zasad jest bardzo istotne, dlatego że cały framework Spring opiera się właśnie na tych mechanizmach. W momencie, kiedy będziemy już skupiali się na API Springa, kiedy będziemy poznawali kolejne moduły, zobaczycie, że na każdym etapie będziemy właśnie wykorzystywali zarówno wstrzykiwanie, programowanie przez interfejsy, jak i programowanie.

Zostaw komentarz

Zostaw komentarz

Zaloguj się
Rejestracja jest darmowa!

Administratorem danych jest Sages Sp. z o.o. z siedzibą w Warszawie przy ul. Nowogrodzkiej 62c. Podanie danych jest dobrowolne. Osobie, której dane dotyczą przysługuje prawo wglądu do danych osobowych, ich zmiany oraz usunięcia w sposób określony w Polityce prywatności.

Please accept the Terms and Conditions to proceed.