2012-05-13

Automatiserad testning av Android-appar

Jag har den här veckan lagt in Robotium som testverktyg för den Android-app, MantisDroid, som jag håller på och utvecklar. Med hjälp av Robotium så kan jag sätta upp helt automatiserade tester av hela appen som kan köras både på emulator och riktig device. Jag har även byggt in stöd för att samtidigt testa mot flera olika versioner av MantisBT (en buggtracker som hanterar defekter för projekt) som är den servertjänst min app går mot.


Robotium är ett testverktyg som kör tester mot användargränssnittet på en app som kör på antingen en emulator eller riktig device. Den simulerar klick på knappar och andra GUI-kontroller, text-hantering i textboxar, den verifierar att olika kontrollera och texter hittas m.m. Kolla på videon ovan för att få en snabb introduktion i hur det fungerar.

Jag har byggt upp mina tester i en klass för varje typ av test, dvs. en klass som hanterar Login-hantering, en för Project och en för Issues.För att hantera flera olika versioner av MantisBT har jag skapat en Enum (MantisBTUrlEnum) som håller i URL:erna för de olika versionerna. Varje testfall loopar över alla värden i Enum:en och plockar ut den URL som ska testas och kör testet mot den. Det är alltså viktigt att återställa miljön efter varje körning så att nästa loop har samma förutsättningar som varvet innan. Om jag varit testpuritan så skulle jag kanske skapat ett testfall för varje version, men jag tycker att även bland testfallen så ska duplicering av kod undvikas. En idé som slog mig nu var att bryta ut själva testet i en egen metod och sedan låta varje testfall anropa den metoden med den URL som ska testas. Nu kommer jag dock att behålla min implementation för den fungerar bra och för tillfället har jag bara en enda URL att testa mot. När det blir fler URL:er så kanske jag bryter isär testfallen om det är fördelaktigt på något sätt.

Tips:

Använd inte hårdkodade strängar. Om ni gör det så kommer testerna att fallera om en sträng ändras eller om ni lägger till fler språk. Använd istället: solo.getString(id);
Ex.
För att hämta strängen "Login" så skriver ni:
solo.getString(se.nextsource.android.mantisdroid.R.string.login);
För att hämta "OK" som ligger inbyggt i Android så skriver ni:
solo.getString(android.R.string.ok);

Jag har stött på följande problem:

Problem - returnerar fel aktivitet vid uppstart:
I setUp() vill jag kontrollera att jag är på rätt aktivitet, men solo.getCurrentActivity() ger fel aktivitet som svar. Den returnerar den aktivitet som angetts vid uppstart. I min applikation anger jag LoginActivity som startaktivitet, men om användaren redan är inloggad så flyttas den automatiskt till MainActivity. Robotium har inte koll på det.
Lösning:
Lägg en sökning efter en textsträng som finns på den aktivitet du önskar starta med i en while-loop. I mitt fall är det en knapp med texten "Login". Loopa tills den strängen hittas. Inuti while-loopen så kontrollerar jag om jag är på MainActivity genom att söka efter en viss textsträng där och om den hittas så tar jag upp menyn och klickar på "Logout":
solo.clickOnMenuItem("Logout"));
I alla andra fall så trycker jag på back-knappen:
solo.goBack();

Problem - hittar inte knappar när soft keyboard är synligt:
När man klickar på en EditTextPreference i en PreferenceActivity så får man på en verklig device upp soft keyboard eftersom textfältet får fokus automatiskt. När soft keyboard är uppe så går det inte att klicka på "OK"-knappen. Knappen hittas med solo.searchButton("OK"); men är inte klickbar. På emulatorn inträffar inte det här eftersom soft keyboard inte kommer fram. Det saknas metoder i Robotium för att kontrollera om soft keyboard är framme eller inte.
Lösning:
Skapa en metod som stänger soft keyboard och använd den då soft keyboard automatiskt poppar upp:

@SuppressWarnings("static-access")
private void closeSoftKeyboard() {
InputMethodManager imm = (InputMethodManager)getActivity().
getSystemService(getActivity().INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(solo.getEditText(0).getWindowToken(), 0);
}

Problem - kan inte klicka på knappar i ActionBar:
Robotium saknar stöd för att klicka på knappar i ActionBar.
Lösning:
Använd Instrumentation och id:et för knappen för att klicka på den:
getInstrumentation().invokeMenuActionSync(solo.getCurrentActivity(), se.nextsource.android. mantisdroid.R.id.menu_settings, 0);