Archiwa tagu: iOS

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.

CrossPlatform Logger

Dziś chciałbym pokazać jak zaimplementować prostego loggera na platformie Xamarin.Forms. Nie znalazłem żadnego dedykowanego rozwiązania oferowanego przez twórców tego Frameworka więc stworzymy dziś bardzo prosty logger,  który pozwoli na logowanie do pliku.

Zaczynamy od utworzenia prostego interfejsu ILogger oraz jego generycznej wersji, która będzie służyć do definiowania kategorii na podstawie typu.

Do zdefiniowania poziomów logowania wykorzystam prostego enuma.

Stworzę teraz klasę FileLogger,  aby umożliwić logowanie na każdej z platform użyje najprostszego mechanizmu, statycznej metody File. AppendAllText. Natomiast odpowiedzialność za tworzenie ścieżki do pliku oraz samej wiadomości przenoszę w inne miejsce za pomocą delegatów. W ten sposób mamy uniwersalny mechanizm,  który możemy wykorzystać na wielu platformach.

Jak widać powyżej pojawił się jeszcze jeden typ LogItem, jest to prosta struktura, która zastąpi przekazanie wielu parametrów do delegatów zgodnie z zasadą, że im mniej parametrów tym lepiej.

Odpowiedzialność za tworzenie ścieżki do pliku oraz samej wiadomości umieściłem w FileLoggerFactory. Klasa ta ma za zadanie stworzyć katalog gdzie zapisywane będą logi oraz zdefiniować delegaty. Oczywiście samo generowanie ścieżki oraz wiadomości jest bardzo proste i niekonfigurowalne, pozostawiam decyzję Tobie czy chcesz użyć jakiejś innej własnej implementacji.

Tak zbudowane rozwiązanie możemy zarejestrować w dowolnym kontenerze Dependency Injection. Jedyne o czym należy pamiętać to, że definicja katalogu gdzie przechowywane będą logi będzie różna dla każdej z platform, poniżej mały przykład z użyciem symboli kompilacji dla różnych platform.

To chyba właściwie wszystko jeśli chodzi o uniwersalne logowanie do pliku dla Androida, iOS-a i UWP przy użyciu framework-a Xamarin.Forms. Każda z platform dostarcza również natywne sposoby logowani ale to nie było celem tego wpisu. Jeśli chcesz dodaj kolejną implementację ILogger, np. AndroidUtilLogger i wykorzystaj Android.Util.Log.Info(„MyApp”, „Hello”).

Cały kod z przykładów znajdziesz na GitHubie.

ShopHelper – Start :)

1,2,3 Start… Rozpoczynam udział w konkursie Daj Się Poznać 2017 🙂

Na początek nie mogło zabraknąć oczywiście wpisu na temat założenia projektu oraz tego co w ogóle będzie robił ShopHelper!

ShopHelper będzie aplikacją mobilną, której główną funkcjonalnością będzie lista zakupów. Lista zakupów powinna być łatwo edytowalna tak aby z ekranu głównego można było dodać nowe pozycje i odhaczyć już zakupione. Dodatkową opcją będzie udostępnianie listy pomiędzy różnych użytkowników, tak aby każdy mógł edytować listę. Kolejna opcja to historia zakupów. W maksymalnym streszczeniu narazie chyba tyle, choć jeśli starczy czasu i inwencji może coś się jeszcze fajnego pojawi 🙂

Jako technologię wybrałem .NET i Xamarin. Planuję aby aplikacja była uruchamia na wielu platformach więc zdecydowałem się użyć framework-a Xamarin.Forms, który pozwoli na równoczesne pisanie kodu pod Androida, iOS-a oraz UWP z użyciem xaml-a. Do pracy z kodem wykorzystam Visual Studio 2015 Update 3 w wersji Community. Zaczynamy!

 

Utworzyłem solucję w VS, nazwałem ją ShopHelper, natomiast sam projekt ShopHelper.Client. Na tą chwilę nie planuję innych komponentów niż aplikacja kliencka ale nigdy nic nie wiadomo, więc w przyszłości nie będę musiał robić refactoringu w nazwach projektów.

Do udostępniania kodu pomiędzy platformy użyję SharedProject, jest to o tyle fajne rozwiązanie, że kod źródłowy jest automatycznie kompilowany razem z każdym osobnym projektem każdej z platform.

Oznacza to tyle, że o tym jak ma zostać skompilowany decyduje projekt, do którego dodana została referencja do SharedProject. Jak widać poniżej po utworzeniu solucji mamy jeden projekt typu SharedProject oraz 3 natywne odpowiadające konkretnym platformą. W skrócie kod „wspólny” umieszczony w ShopHelper.Client korzysta z framework-a Xamarin.Forms, następnie każdy z projektów z aplikacją natywną pod każdą z platform  posiada referencję do SharedProject oraz wszystkich potrzebnych bibliotek.

Tak wygląda solucja zaraz po utworzeniu:

Cała „filozofia” współdzielenia kodu pomiędzy różne platformy polega na tym, że każda z platform wykorzystuje wspólny komponent App, ładując go w odpowiedni dla konkretnej platformy sposób. Poniżaj klasa App oraz załadowanie jej przez natywną aplikację Android.

Po skompilowaniu i uruchomieniu aplikacja na razie nic nie robi i wygląda tak na tablecie z Androidem:

Podsumowując dziś udało mi się stworzyć „pusty” projekt, który pozwoli na pisanie kodu pod różne platformy, kod oczywiście udostępniam na GitHubie, w następnych dniach utworzę pierwsze prawdziwe komponenty i podzielę się nimi w następnych wpisach. Ufff jak na pierwszy wpis poszło mam nadzieję nieźle, i że nic istotnego nie pominąłem. Zachęcam do zostawienia komentarzy oraz lektury następnych wpisów już wkrótce!