Archiwa tagu: MVVM

ShopHelper – Unit testy

Poprzedni wpis na temat projektu ShopHelper: lokalna lista zakupów

No i przyszedł czas na testy jednostkowe. Być może trochę późno powiedzą fani TDD 🙂 Ogólnie dodając pierwsze komponenty starałem się mieć gdzieś z tyłu głowy, że będę testy pisał ale jak to bywa nie wszystko się odrazu wie… Ten wpis będzie tak naprawdę o dostosowaniu projektu do testów jednostowych i małym refactorze, który musiałem zrobić aby móc testować 🙂

Pierwszy być może mały błąd popełniłem decydując się na SharedProject zamiast Portable class library. Dlaczego?

Rozwiązaniu typu SharedProject kompiluje się razem z binarką, do której został dołączony, oznacza to, że tak naprawdę nasz kod nie kompiluje się do samodzielnej biblioteki. Wyjścia są dwa, a może i nawet trzy. Pierwsze dodać po projekcie do każdej platformy czyli Android.Tests, UWP.Tests, iOS .Tests – masakra. Drugie dodać sztuczną bibliotekę typu portable, do której dołączymy nasz SharedProject, następnie tą bibliotekę podłączymy do naszego projektu z testami. Trochę sztuka dla sztuki ale powinno się udać. Zdecydowałem się na trzecie rozwiązanie – zwykłe klasyczne windowsowe class library (oczywiście jeśli ktoś programuje na Mac-u to rozwiązanie odpada). Problem jaki wynika z tego rozwiązanio to tak naprawdę to, że nasz kod musi obsługiwać tak naprawdę platformę Windows Desktop…  Spowodowało to konieczność wydzielenia poszczególnych implementacji komponentów specyficznych dla danej platformy, jak np. ścieżki do plików. Zaimplementowałem to tak, że we wspólnym kodzie dodałem interfejs IFileService (wcześniej korzystałem ze statycznego Utils-a). W natywnych projektach dla każdej z platform dodałem po klasie implementującej ten interfejs (również do projektu z testami).

Przykład dla Androida:

Kolejny typowy problem, który się pojawił to, że nie wstrzykiwałem zależności do komponentów oraz nie korzystałem z interfejsów co utrudnia Mockowie. Jak napisać test sprawdzający czy wywołano metodę Show, która ma pokazać komponent listy?

Raczej się nie da, pomijając już, że metoda jest prywatna ale taka miała być ponieważ jest podpięta to Command, więc test planowałem wykonać porzez Command.Execute(null). Jednak to się też nie uda ponieważ nie wstrzykuję ani Command ani ViewModel-u, który trzyma Command… Zrobiłem refactor tego komponentu no i udało się osiągnąć zamierzony efekt 🙂

Kod jest prosty, a zarazem napisanie testu sprawdzającego czy komenda ShowShoppingListCommand otwiera komponent ShoppingList to formalność.

Do obsługi testów skorzystałem z Framework-a NUnit oraz Moq. Natomiast sam test jest napisany zgodnie ze wzorcem Arrange-Act-Assert co pozwola na czytelne formatowanie kodu testów. W sekcji arrange umieściłem kod odpowiedzialny za stworzenie obiektów potrzebnych do przeprowadzenia testu. Sekcja Act to tylko wywołanie testowanego polecenia. Assert sprawdza czy na Mock-u wykonano dokładnie raz metodę Show. Jak pewnie zauważyłeś testy wymusiły na mnie dodanie interfejsów również dla widoków, dzięki czemu w testach nie korzystam z prawdziwych widoków tylko mockuje interfejs, dzięki czemu testy są zupełnie odseparowane od warstwy prezentacji.

Na koniec tego wpisu jeszcze inny przykład testu:

Test ma za zadanie sprawdzić czy polecenie AddShoppingListItem wykonuje dodanie elementu do usługi przechowującej dane oraz czy element jest dodawany do listy bindowanej do widoku. Na koniec czy wartość NewShoppingListItemValue jest czyszczona. Przy okazji zacząłem się zastanawiać czy nie zacząć utrzymywać kolekcji ObservableCollection, która jest bindowana do widoku tylko w serwisie.

Właśnie na tym polega magia testów jednostkowych, testy nie tylko pomagają znaleźć błędy ale jeśli zastosujemy technikę TDD to pomagają pisać lepszy kod! Warto już na początku projektu zacząć pisać testy, dzięki temu unikniemy późniejszych bolesnych zmian. Oczywiście refator to powinna być nieodłączna czynność w TDD ale warto już na starcie przygotować nasz projekt tak aby był łatwo testowalny:

  • zapewnić odpowiednią abstrakcję poprzez interfejsy tam gdzie będziemy chcieli skorzystać z Mocków
  • generalnie nie twórzmy samodzielnie zależności tylko wstrzykujmy je z zewnątrz, np. poprzez konstruktor jako abstrakcję
  • jak najmniej starajmy się korzystać ze statycznych Utilsów, Helpersów itp. ponieważ zmockowanie takiej metody jest bardzo trudne, nawet zamiast DateTime.Now zalecałbym jakiś interfejs albo własny obiekt, który na potrzeby testu można podmienić

To chyba na dziś wszystko, zapraszam do komentowania 🙂 Pełny kod znajdziesz na GitHubie.

ShopHelper – Pierwsze kroki i trochę o MVVM

Poprzedni wpis na temat projektu ShopHelper: Start

W tym artykule chciałbym zaprezentować pierwszą implementację komponentów UI. W poprzednim poście zakończyliśmy na założeniu solucji i dodaniu domyślnych komponentów z użyciem frameworka Xamarin.Forms. Naszym celem na tym etapie jest:

  • stworzenie podstawowych wspólnych komponentów z użyciem wzorca MVVM (Model-View-ViewModel)
  • zapewnienie podstawowej nawigacji
  • wyświetlenie prostej listy zakupów z możliwością dodawania nowych elementów.

Zaczynamy od stworzenia dwóch klas i jednego interfejsu, które pomogą nam modelować komponenty według wzorca MVVM, oczywiście nie jest to wymagane ale bardzo uspójni architekturę i implementację przyszłych komponentów aplikacji. Tak więc tworzymy interfejs IView:

Interfejs ten jak widać powyżej zawiera tylko jedną właściwość BindingContext, która zapewni  „połączenie” pomiędzy widokiem, a ViewModel. Generalnie interfejs ten powinny implementować wszystkie widoki naszej aplikacji. Czym są widoki? To nic innego jak graficzne komponenty, które udostępnia nam framework Xamarin.Forms, może to być m.in. ContentPage, ContentView lub inne kontrolki… Następnie dodajmy klasę abstrakcyjną ViewModel oraz generyczny ViewModel:

Przedstawiony ViewModel posłuży nam do implementacji bazowego mechanizmu eksponującego model dla widoków. Mówiąc inaczej to w tym miejscu (w klasach pochodnych) znajdą się właściwości, które chcemy udostępnić do warstwy prezentacji. ViewModel implementuje interfejs INotifyPropertyChanged, za pomocą tego interfejsu możemy powiadomić widok, że jakaś wartość jednej z naszych właściwości uległa zmianie. Dzięki takiemu rozwiązaniu otrzymujemy rozdzielenie warstwy prezentacji (widoków) od modelu / logiki. I tak zadaniem widoku jest wyłącznie prezentować dane, nie wykonuje on żadnej logiki.

Przejdźmy teraz do stworzenia podstawowych elementów naszej aplikacji. Każda aplikacji musi mieć jakiś główny komponent, nazwę go po prostu Main. Tworzę więc katalog Main i umieszczam w nim klasę MainComponent, to tu umieszczę logikę związaną z tym komponentem, myślę tu głownie o nawigacji do innych komponentów aplikacji. W tym samym katalogu tworzę MainView.xaml używając szablonu oferowanego przez Visual Studio.

Tak jak wspomniałem wcześniej każdy widok powinien implementować interfejs IView:

Dzięki temu, że ContentPage dziedziczy po BindableObject, w którym już znajduje się właściwość o tej samej nazwie nic więcej nie musimy implementować. W konstruktorze naszego bazowego ViewModel wykona się cała „magia” MVVM – ViewModel zostanie powiązany z widokiem. Następnie utworzyłem klasę MainViewModel, która dziedziczy po generycznym ViewModel. Zwróćmy uwagę jak ViewModel udostępnia informacje do widoku, zobaczmy na właściwości Title oraz ShowShoppingListCommand udostępniane przez nasz MainViewModel:

Jak widać powyżej w widoku mamy odwołanie do tych właściwości poprzez wykorzystanie mechanizmu Binding (wiązania). Dodałem w MainView również jakąś prostą nawigację, wykorzystałem do tego kontrolkę StackLayout i trzy przykładowe przyciski, pod jeden z nich podpinam właśnie wspomniany wcześniej ShowShoppingListCommand, to nic innego jak implementacja System.Windows.Input.ICommand – komendy, która zapewnia przekazanie wykonania do wskazanej metody. Właściwość ShowShoppingListCommand korzysta z metody udostępnianej przez bazowy ViewModel SetProperty. Zadaniem tej metody jest ułatwić wykorzystanie notyfikacji INotifyPropertyChanged i skrócić kod przypisania nowej wartości do jednej linii. Dzięki takiemu mechanizmowi przypisując wartość naszej właściwości powiadamiamy widok o zmianie.

Nową instancję komponentu Main tworzymy w klasie App zaraz po uruchomieniu aplikacji, do metody Run przekazujemy obiekt Application. W metodzie Run uruchomimy nawigację oferowaną przez Xamarin.Forms dodając nasz widok do globalnej nawigacji. W skrócie MainComponent rozpoczyna nawigację globalną dla całej aplikacji oraz tworzy polecenia definiując metody do nawigowania do innych komponentów.

Teraz czas na komponent ShoppingList czyli naszą na razie prostą listę zakupów. Tworzę katalog ShoppingList, a w nim umieszczam kolejno ShoppingListView, ShoppingListViewModel oraz ShoppingListComponent. Widok listy zakupów przedstawia się następująco:

Dzięki prostemu zastosowaniu kontrolek oferowanych przez Xamarin.Forms umieściłem wszystkie komponenty w StackLayout co pozwoli na rozmieszczenie elementów pod sobą na całym ekranie. Dodałem listę, a pod nią kontrolkę Entry, w której możliwe będzie wprowadzanie nowych pozycji. Obok umieściłem przycisk, którym użytkownik będzie mógł dodawać pozycje do listy. Na razie jest bardzo prosto ale tak miało być :). ShoppingViewModel ma za zadanie eksponować właściwości dla widoku i tak też robi, nic innego się tam nie dzieje:

Tu można zauważyć, że pojawiła się jeszcze jedna klasa, jest to ShoppingListItem, na razie bardzo prosty model. ViewModel udostępnia kolekcję ObservableCollection<ShoppingListItem>, dzięki czemu po każdym dodaniu, usunięciu elementów z kolekcji widok zostanie automatycznie powiadomiony o zmianach.

Zadaniem komponentu ShoppingList jak na razie jest stworzyć wszelkie potrzebne dla widoku powiązania i zapewnić obsługę listy zakupów. Jak na razie obsługujemy tylko dodawanie nowych elementów do listy, dzieje się to w metodzie AddShoppingListItem, która została podpięta do AddShoppingListItemCommand. Tak więc zbieramy wprowadzoną przez użytkownika wartość pola NewShoppingListItemValue, jeśli nie jest pusta to dodajemy nowy element do kolekcji i czyścimy właściwość, czyszcząc w ten sposób kontrolkę Entry.

Na początek to chyba tyle 🙂 wychodzę z założenia, aby iść małymi krokami. Dziś chciałem też napisać trochę o MVVM ponieważ korzystając z Xamarin.Forms po prostu nie wypada inaczej, a dzięki zachowaniu prostych zasad tego wzorca nasza aplikacja będzie dobrze napisana. Cały kod znajdziecie oczywiście na GitHubie.