Automatyczne testowanie aplikacji mobilnych za pomocą narzędzi Xamarin

Autorzy: Tomasz Soroka

23.09.2016

Wprowadzenie

Opis genezy tego artykułu najlepiej rozpocząć od podstawowego pytania. Mianowicie, dlaczego testowanie aplikacji mobilnych jest ważne? Otóż, na podstawie badań przeprowadzonych w USA, oszacowano, że aby aplikacja pokrywała zasięgiem 80% rynku mobilnego, powinna zostać przetestowana na minimum 134 różnych urządzeniach. Badanie to dotyczy dwóch najpopularniejszych platform – iOS i Androida (Rysunek 1).


Rysunek 1. Wykres udziału aplikacji w rynku w zależności od liczby urządzeń wykorzystanych do testów. Jak widać, wykres ma przebieg wykładniczy. Oznacza to, że procentowe pokrycie rynku szybko rośnie z liczbą urządzeń wykorzystanych do testów.


Według innego badania, jeżeli aplikacja nie załaduje się w ciągu trzech sekund od momentu jej uruchomienia, to 60% użytkowników zamknie ją, a co gorsza 43% użytkowników deklaruje, ze nigdy do takiej aplikacji nie powróci.Z drugiej strony dobrze działająca aplikacja zostanie polecona dalej przez ponad 45% użytkowników.

 

Aplikacje mobilne są łatwo dostępne, wystarczy kilka kliknięć, aby pobrać i uruchomić je z odpowiedniego sklepu. Wybór aplikacji jest przeogromny i jakość jest słowem kluczem przy ich tworzeniu, gdyż to właśnie jakość decyduje o powodzeniu konkretnej aplikacji. Jednym z elementów zapewnienia jakości jest odpowiednie testowanie. W przypadku aplikacji mobilnych nie jest to jednak zadanie łatwe. Jednym z zasadniczych problemów jest różnorodność ekranów urządzeń mobilnych (Rysunek 2) oraz bardzo duże rozdrobnienie rynku, które obejmuje:

  • różne wersje systemów operacyjnych (IOS, Android, Windows Phone, Windows 10),
  • różne wersje urządzeń (smartfony, tablety),
  • różne rodzaje smartfonów (różne rozdzielczości, wielkości ekranów, rodzaje procesorów, ilości pamięci),
  • różne rodzaje tabletów (różne rozdzielczości, wielkości ekranów, rodzaje procesorów, ilości pamięci),
  • różne wersje systemów tego samego producenta np (Android od wersji 2.3 do wersji 6.X, iOS od wersji 7 do wersji 9.X, Windows Phone od 8.0 do wersji 10).


Rysunek 2. Różnorodność wielkości ekranów urządzeń mobilnych


Biorąc pod uwagę ilość możliwych kombinacji problemów, które mogą się pojawić w trakcie tworzenia aplikacji, okazuje się iż wykonanie prawidłowo działającej aplikacji jest sporym wyzwaniem. Z tego powodu przetestowanie aplikacji staje się bardzo dużym wyzwaniem dla developera.


Metody testowania aplikacji mobilnych


Jest bardzo dużo sposobów testowania aplikacji mobilnych, poczynając od testów manualnych - popularnego klikania, poprzez crowdtesting, a kończąc na testach automatycznych. Tak samo dużo jest obszarów testowania aplikacji mobilnych, poczynając od testów jednostkowych (Unit Tests), poprzez testy integracyjne, testy systemowe, a kończąc na automatycznych testach interfejsu użytkownika aplikacji (Automated UI Testing), w których oprogramowanie symuluje typowe czynności wykonywane przez użytkownika, z wykorzystaniem interfejsu użytkownika, np. klikanie (dotykanie) przycisków, wybór pozycji z list rozwijanych, przewijanie widoków, itp.

Rysunek 3 ilustruje porównanie różnych scenariuszy testowych. Ręczne testy są najbardziej realistyczne, aczkolwiek wymagają długiego czasu realizacji poszczególnych iteracji. Tradycyjne testy jednostkowe pozwalają skrócić czas pojedynczego cyklu kosztem zredukowanego realizmu testu. Automatyczne testy UI zapewniają wysoki realizm, a jednocześnie skracają czas wykonywania danego testu. Na podstawie tego porównania widzimy, że automatyczne testy UI wydają się być dobrym rozwiązaniem podczas rozwoju aplikacji. Z tego powodu w tym artkule skupimy się na automatycznych testach UI. W tym celu omówione zostanie zastosowanie narzędzi firmy Xamarin, które mogą być wykorzystane do testowania aplikacji mobilnych wykonanych za pomocą narzędzi tej firmy, lecz nie tylko.

Rysunek 3. Charakterystyka testów aplikacji.


Narzędzia dostarczane przez firmę Xamarin

Xamarin dostarcza dwa podstawowe narzędzia do prowadzenia testów automatycznych. Pierwszym z nich jest framework do pisania testów (Xamarin.UITest). Umożliwia on napisanie kodu, który emuluje zachowanie użytkownika i pozwala na wykonanie testu na emulatorze lub urządzeniu, które jest podłączone do komputera. Drugim narzędziem jest Xamarin Test Cloud (XTC). Narzędzie, które umożliwia wykonanie wcześniej napisanego testu w chmurze na ponad 2000 fizycznych urządzeń.

 

Rodzaje testów, które można wykonać za pomocą narzędzi Xamarin

Podstawowym celem narzędzi firmy Xamarin jest automatyzacja procesu testowania oraz wykonywanie testów na dużej ilości różnorodnych urządzeń. W podstawowym podejściu testy są projektowane, uruchamiane i dostosowywane lokalnie,  w szczególności, na fizycznym urządzeniu podłączonym do komputera lub emulatorze, następnie ten sam test może zostać opublikowany i wykonany w chmurze XTC na setkach wybranych urządzeń.

 

Jak pisze się testy za pomocą narzędzi firmy Xamarin?

Pisanie testu odbywa się za pomocą tworzenia skryptów w języku C# lub Ruby. Skupimy się na teście napisanym w języku C#. Stworzymy test, który sprawdzi, czy przycisk start mieści się na ekranie urządzenia mobilnego. Na potrzeby konkretnego przykładu stworzyliśmy stosunkowo prostą aplikację wykonaną w technologii Xamarin Forms. Aplikacja ta składa się z dwóch ekranów. Na pierwszym ekranie aplikacji znajduje się przycisk z etykietą Start app, którego jedynym zadaniem jest przeniesienie użytkownika do drugiego ekranu (nawigacja do innego widoku), który wyświetla napis o treści Hello!. Oba widoki aplikacji ilustrują zrzuty z Rysunków 4-6.

Rysunek 4. Ekran powitalny aplikacji, uruchomionej w emulatorze GenyMotion


Rysunek 5. Drugi widok aplikacji


Rysunek 6. Ekran początkowy w trybie poziomym


Stwórzmy zatem test, którego zadaniem będzie uruchomienie aplikacji, obrócenie ekranu do orientacji poziomej i naciśnięcie przycisku z etykietą Start app. Spowoduje to wyświetlenie drugiego ekranu aplikacji z napisem Hello. Ostatnim krokiem jest powrót do ekranu początkowego aplikacji. 

Kod źródłowy testu jest przedstawiony na Listingu 1.


Listing 1. Kod źródłowy testu UI

[Test]
        public void AppScreensPortraitAndLandscape ()
        {
            app.SetOrientationPortrait ();
            System.Threading.Thread.Sleep (3000);
            app.Screenshot ("Start page in portrait modee");
            app.WaitForElement (c => c.Button("Start app"));
            app.SetOrientationLandscape ();
            System.Threading.Thread.Sleep (3000);
            app.Screenshot ("Start page in landscape mode");
            System.Threading.Thread.Sleep (3000);
            app.WaitForElement (c => c.Button("Start app"));
            app.Tap (p => p.Button ("Start app"));
            app.WaitForElement (c => c.Text("Hello!"));
            app.Back ();
            System.Threading.Thread.Sleep (3000);
            app.Screenshot ("Start page back");
        } 


Jak można zauważyć powyższy test składa się z bardzo prostych komend, które symulują operacje wykonywane przez użytkownika. Mianowicie, polecenie app.SetOrientationPortrait() ustawia ekran urządzenia mobilnego w trybie pionowym. W kolejnym kroku komenda System.Threading.Thread.Sleep(3000) usypia wykonanie testu na 3 sekundy. Tego typu opóźnienie jest przydatne w sytuacji gdy testujemy wydajność aplikacji w celu weryfikacji czy konkretna operacja wykona się w założonym czasie. W tym przypadku w ciągu 3 sekund. Następnie, za pomocą metody app.SetOrientationLandscape() zmieniamy orientację urządzenia, po czym funkcja app.Screenshot("Start page in portrait mode") wykonuje zrzut ekranu i zapisuje go na dysku.  Dzięki temu możemy zapisać wizualny status aplikacji w celu sprawdzenia, jak wygląda dany widok w konkretnym urządzeniu z chmury Xamarin. Zadaniem polecenia app.WaitForElement (c => c.Button("Start app")) jest sprawdzenie, czy na ekranie urządzenia widoczny jest przycisk z etykietą Start app. Następnie, symulujemy kliknięcie (dotknięcie) tego przycisku za pomocą polecenia app.Tap (p => p.Button ("Start app")). Po czym wykonywana jest nawigacja do początkowego ekranu (metoda app.Back) i ponownie, po odczekaniu 3 sekund, zapisujemy zrzut ekranu na dysku.

 

Rysunek 7 ilustruje widok środowiska Xamarin Studio. W głównej części rysunku widoczny jest kod źródłowy utworzonego testu. Z lewej strony widać strukturę projektu, a z prawej okno umożliwiające uruchomienie testu. Po kliknięciu przycisku Run All nastąpi uruchomienie testu. Przykładowe wyniki uzyskane za pomocą emulatora Galaxy Nexus 4.3 (okno Unit Tests z Rysunku 7)  pokazują, że test wykonał się bez problemu. Jednakże pozostaje pytanie – co w przypadku innych urządzeń dostępnych na rynku?

Rysunek 7. Widok środowiska Xamarin Studio.


Uruchamianie testu w chmurze Xamarin Test Cloud

 

W celu uzyskania odpowiedzi na pytanie postawione w poprzednim podrozdziale należy uruchomić test na większej liczbie urządzeń. Test z Lisingu 1 nadaje się do tego celu idealnie.

Xamarin Test Cloud jest chmurą ponad dwóch tysięcy urządzeń mobilnych (z systemami Android oraz iOS), na których developer może uruchamiać jednostkowe testy UI (Rysunek 8). Bezpośrednio przed każdym uruchomieniem testu można wybrać docelowe urządzenia. Następnie, następuje wysłanie skompilowanej aplikacji oraz testu do chmury Xamarin, a po wykonaniu testu, jego wyniki są prezentowane w formie bardzo atrakcyjnego raportu (Rysunek 9). Dodatkowo, mamy możliwość wizualnej inspekcji poszczególnych widoków aplikacji zapisanych za pomocą metody app.Screenshot (Rysunek 10 i Rysunek 11).

Rysunek 8. Wybór urządzeń w Xamarin Test Cloud, na których zostanie wykonany test



Na potrzeby opisywanego przykładu test z Listingu 1 uruchomiłem na 213 różnych urządzeniach z systemem operacyjnym Android (pełny raport jest dostępny pod adresem: https://testcloud.xamarin.com/test/xtcexampleapp_c4b44135-9331-496c-a7db-bd9f8355e783). Wyniki są takie iż na dużej liczbie urządzeń test nie wykonał się prawidłowo (Rysunek 9). Powodem jest to iż na urządzeniach o mniejszej wielkości / gęstości ekranu, w trybie poziomym, przycisk z etykietą Start app nie zmieścił się na ekranie. Czyli komenda: app.WaitForElement (c => c.Button("Start app")) nie została wykonana prawidłowo (Rysunek 12 i Rysunek 13). Oczywiście jest to ewidentny błąd developera – aplikacja powinna zostać zaprojektowana w taki sposób aby przycisk z etykietą Start app był zawsze widoczny. Chociaż ten błąd został wygenerowany na potrzeby niniejszego artykułu, to już wiele razy w codziennej pracy spotkaliśmy się z aplikacjami, które miały błędy tego typu.  Zazwyczaj scenariusz był taki, że developer tworzył aplikację, która była testowana w sposób manualny na urządzeniach / emulatorach, do których developer miał dostęp – zazwyczaj 2-3 urządzenia. Następnie aplikacja była publikowana i okazywało się, że na dużej części urządzeń nie działała ona prawidłowo. Developer nie miał pojęcia dlaczego tak się dzieje, ze względu na brak możliwości przetestowania jej na większej ilości urządzeń.

Rysunek 9. Wynik testu. Test nie wykonał się na 20 urządzeniach



Rysunek 10. Widok pierwszego kroku testu - wykonał się prawidłowo na wszystkich 213 urządzeniach


Rysunek 11. Trzeci krok testu. Test nie wykonał się na 20 urządzeniach, gdyż przycisk z etykietą Start nie jest na nich widoczny.


Rysunek 12. Szczegóły błędu



Rysunek 13. Zrzut ekranu z prawdziwego urządzenia – widać "ucięty" przycisk z etykietą Start app



Jak korzystać z narzędzi Xamarin, wspierających automatyczne testy? 

Strategii testowania jest bardzo wiele i wybór odpowiedniej zależy od wielu czynników. Proces testowania aplikacji mobilnych w Leaware składa się z kilku faz:

  1. Opracowanie scenariusza testu – specyfikacja, co chcemy przetestować na danym ekranie aplikacji – jakie zachowania, które elementy itd., czego się spodziewamy
  2. Zaprogramowanie testu – napisanie skryptu realizującego opracowany scenariusz
  3. Wykonanie testu na urządzeniu fizycznym podpiętym lokalnie w ramach infrastruktury Leaware
  4. Sprawdzenie wyników testu i adaptacja
  5. Publikacja i wykonanie testu w chmurze Xamarin Test Cloud na wielu urządzeniach - w tym przypadku trzeba wspomnieć, że utylizacja każdej minuty chmury XTC jest płatna, dlatego warto się zastanowić jak najlepiej wykorzystać dostęp do chmury
  6. Dokumentacja wyników testów – w postaci raportów z XTC


Testy funkcjonalne, wykrywanie błędów regresywnych

 

W testowaniu funkcjonalnym tworzymy testy badające funkcjonalności tworzonego rozwiązania. Skupiają się one głównie na części związanej z wykonywaniem zaprogramowanych akcji/funkcji aplikacji w odpowiedzi na interakcję z użytkownikiem. Symulujemy działanie osoby, która używa aplikację i badamy czy zachowanie aplikacji jest zgodne z wymaganiami funkcjonalnymi. Pokrywamy testami wszystkie najważniejsze scenariusze wykorzystania aplikacji. Dodatkowo podczas prac nad aplikacją w przypadku wykrywania błędów piszemy testy, których zadaniem jest sprawdzanie z jednej strony, czy wdrożana poprawka usuwa wykryte błędy, a z drugiej zapewnienie, żeby te błędy nie pojawiały się w przyszłości – nie będzie regresji.

 

Strategia opracowywania i uruchamiania takich testów jest bardzo podobna:

  1. Opracowanie scenariusza testu – dla testów funkcjonalnych na podstawie dokumentacji funkcjonalnej lub w przypadku wykrycia błędu na podstawie scenariusza odtworzeniowego błędu
  2. Zaprogramowanie testu – napisanie skryptu realizującego opracowany scenariusz
  3. Publikacja testu na serwerze integracji ciągłej (Continuous Integration)
  4. Dokumentacja wyników testów – w postaci raportów wykonania testów na poszczególnych buildach (wydaniach) aplikacji CI

 

Dzięki zastosowaniu serwera CI testowanie w Leaware zostało jeszcze bardziej zautomatyzowane. Testy są publikowane razem z repozytorium kodu źródłowego. Testy te uruchamiane są na urządzeniach lokalnych dostępnych w ramach naszej infrastruktury. Domyślny scenariusz jest taki iż serwer CI sprawdza raz na dobę, czy w trakcie dnia prowadzone były prace developerskie na kodzie aplikacji. Jeżeli tak, to tworzony jest testowy build, na którym wykonują się automatycznie wszystkie napisane do tej pory testy. W przypadku niepowodzenia wykonania jakiegokolwiek z testów, informacja ta trafia do developerów poprzez różne kanały informacyjne – komunikatory, poczta elektroniczna, powiadomienia, dzięki czemu możliwa jest bardzo szybka reakcja i poprawienie kodu. Dodatkowo, każdy z developerów może na żądanie (ad-hoc) uruchomić wszystkie lub dowolne z przygotowanych testów i lokalnie przejrzeć ich wyniki.

  

Wykonywanie testów wizualnych

Testy te mają na celu sprawdzenie, czy ekrany tworzonej aplikacji wyglądają prawidłowo – czy nie ma poważniejszych problemów z wyglądem na różnych urządzeniach. Są to specjalnie przygotowane testy, które skupiają się na przejściu przez wszystkie ekrany aplikacji.

Testy te uruchamiamy w chmurze XTC przed końcem każdego sprintu. Wybieramy kilka urządzeń, które pokrywają najczęściej wykorzystywane rozdzielczości i gęstości urządzeń. Następnie wyniki testów weryfikowane są przez dział quality assurance.

 

Wykonywanie testów badających różnice między urządzeniami

Scenariusz w którym uruchamiamy wybrane testy na dużej ilości urządzeń w XTC. Cel jest taki, aby uruchamiać testy na np. Na najszybszych i najwolniejszych urządzeniach, na urządzeniach o z dużą i małą rozdzielczością, z dużą i małą ilością pamięci operacyjnej. Cel jest taki aby wykrywać potencjalne problemy związane z tymi czynnikami.

Testy te wykonywane są przed oddaniem nowej wersji aplikacji do klienta.

 

Badanie wydajności aplikacji mobilnej

Testy, których celem jest sprawdzenie wydajności aplikacji mobilnej. W tym celu uruchamiamy specjalnie napisane testy na dużej ilości urządzeń. Dzięki temu możemy sprawdzić jak aplikacja zachowuje się, gdy działa równocześnie na np. 200 urządzeniach.

Testy te wykonujemy przed oddaniem nowej wersji aplikacji do klienta.

 

Ograniczenia narzędzi do testowania

Podstawowym ograniczeniem Xamarin.UITest jest brak możliwości wykonywania pewnych operacji w sposób automatyczny. Przykładowo nie mamy możliwości odpowiadania na pewne zdarzenia systemowe (np. akceptacja uprawnień aplikacji do obsługi komunikatów PUSH), czy symulowania sieci 3G. Jest kilka sposobów obchodzenia tych ograniczeń. Można na przykład symulować wykonanie zdjęcia i przekazywać aplikacji gotowe zdjęcie z pamięci urządzenia (Listing 2). W procesie testowania oprogramowania bardzo przydatne są również sposoby interakcyjnego przeprowadzania testów, gdzie podczas działania testu potrzebna jest ingerencja testera. Mimo, że nie uzyskamy w takim przypadku pełnej automatyzacji, to niewątpliwą zaletą jest, iż test nawet z udziałem testera jest wykonywany za każdym razem w taki sam sposób – jest powtarzalny i większa część testu jest wykonywana w sposób automatyczny.


[Test]
        public void FoldersInOfflineMode()
        {
            string randomfolderName = "aat_"+DateTime.Now.ToString("yyyyMMddHHmmssfff");

            app.Screenshot("Open Application");
            app.WaitForElement(p => p.Id("content"), "Login Page doesn't load", new TimeSpan(0,0,15));
            app.Screenshot("Login Page");
            app.Tap(e => e.Class("EntryEditText").Index(1));
            app.EnterText(userLogin);
            app.Tap(e => e.Class("EntryEditText").Index(2));
            app.EnterText(userPassword);
            
            app.Screenshot("Enter Email and Password");
            app.Tap(p => p.Text("Login"));
            app.Screenshot("After Click On Login Button");
            app.WaitForElement(p => p.Id("action_bar_title"), "Home Page doesn't load", new TimeSpan(0,0,35));
            app.Screenshot("Home Page");
            app.Tap(x => x.Text(mainFolder2));
            app.Screenshot("Enter into folder");
            app.ScrollDownTo(mainFolder2TestImg1,null,0,0.67,500,true,new TimeSpan(0,0,100));   
            app.Tap(mainFolder2TestImg1);


            app.Repl();
            //CHECK: wait as long as image will load
            //CHECK: turn on flight mode
            //CHECK: type quit in Repl

            app.Back();
            app.Back();
            app.Tap(x => x.Text(mainFolder2));
            app.ScrollDownTo(mainFolder2TestImg1,null,0,0.67,500,true,new TimeSpan(0,0,100));   
            app.Tap(mainFolder2TestImg1);
            app.Repl();
            //CHECK: wait as long as image will load
            //CHECK: check if image was loaded
            //CHECK: turn off airplane mode
            //CHECK: type quit in Repl

        } 

Listing 2. Przykład testu półautomatycznego - test zatrzymuje się na komendzie app.Repl() i czeka na interakcję ze strony testera


Podsumowanie

Sposobów użycia wyżej opisanych narzędzi jest dużo więcej niż te, które zostały przedstawione w niniejszym artykule. Jeżeli czytelnicy wyrażą zainteresowanie, z przyjemnością przestawimy „przypadki z życia wzięte”, w których wykorzystanie Xamarin.UI.Test oraz Xamarin Test Cloud uratowały nie jeden projekt, który został przejęty po innej, zagranicznej, firmie developerskiej i w którym były początkowo bardzo duże problemy związane z jakością kodu i dużą ilością błędów regresywnych. Dodatkowo bardzo ciekawym zagadnieniem jest integracja narzędzi Xamarin z procesem ciągłej integracji (continuous integration).  Nie jest to zadanie proste, lecz daje bardzo dużo korzyści, oszczędności czasu i poprawy jakości rozwiązań tworzonych w firmie.