Sooo. Für sämtliche Verzögerungen entschuldige ich mich schon mal im Voraus.
(Mein ordentliches Notebook mit einem reinen 64 Bit Linux kann nicht zur Entwicklung genutzt werden, da Teile des SDK zwingend 32 Bit erfordern und die notwendigen Bibliotheken nicht in 32 Bit bei der Distribution vorliegen. Also quäle ich mich mit Windows 10 auf 1.33GHz, 2GB RAM und 32GB SD rum. Kann man mit arbeiten, wenn man auf AVDs verzichtet. Dauert allerdings alles ein wenig länger.)
Um mit dem UI Test zu beginnen, brauche ich persönlich nicht einmal ein existierendes UI, obwohl natürlich eine Defaultactivity angelegt wurde.
Ich teste immer erst alles, bevor ich es baue.
Klingt unlogisch, denn der Test kann ja nur fehlschlagen, wenn ich noch nichts programmiert habe. Aber so kann ich sehen, dass mein Test auch wirklich ausgeführt wird. Fehlschlagende Tests sind kein Problem, solange sie irgendwann wieder laufen. Nur vor Auslieferung müssen alle Tests durchlaufen.
Zunächst fange ich mit drei Tests an:
- Ist auch wirklich die richtige Activity offen?
- Verhält sich der 'Kommen' Knopf so, wie ich es erwarte?
- Verhält sich der 'Gehen' Knopf so, wie ich es erwarte?
Wie ihr euch vorstellen könnt, machen die implementierten Tests (Branch 01UITest) noch nichts sinnvolles. Die wichtigen Ausgaben aus dem Log:
Zitat
java.lang.AssertionError: Not Yet Implemented
at de.marcofeltmann.coding.timetracker.TimeRecordingActivitiyUITest.startButtonPressed(TimeRecordingActivitiyUITest.java:32)
java.lang.AssertionError: Not Yet Implemented
at de.marcofeltmann.coding.timetracker.TimeRecordingActivitiyUITest.endButtonPressed(TimeRecordingActivitiyUITest.java:38)
java.lang.AssertionError: Not Yet Implemented
at de.marcofeltmann.coding.timetracker.TimeRecordingActivitiyUITest.correctActivityOpened(TimeRecordingActivitiyUITest.java:26)
22:48:52 Tests Failed: 0 passed, 3 failed
Alles anzeigen
Sieht nach unnützen Informationen aus, aber ich sehe, dass die Tests aufgerufen werden und funktionieren (a.k.a. fehlschlagen).
Nun fange ich an zu prüfen, ob die Activity die richtige ist. Mangels Kreativität vergleiche ich den Titel der Activity mit dem hinterlegten Titel in der Sprachdatei.
So vermeide ich auch Probleme beim Testen auf unterschiedlich lokalisierten Geräten.
Zusammengezimmert sieht das Ganze dann so aus:
@Test
public void correctActivityOpened() {
String activityTitle = mActivityRule.getActivity().getTitle().toString();
assertEquals( mActivityRule.getActivity().getResources().getString(R.string.app_name), activityTitle );
}
Zitat
22:51:33 Tests Failed: 1 passed, 2 failed
Und schon ist 1/3 der Fehler beseitigt.
Wahnsinn, seit ungefähr 3 Stunden gewerkelt, Null Produktivcode geschrieben und schon einen "Fehler" beseitigt. Wenn das nur immer so einfach wäre.
Ist es aber natürlich nicht. Denn jetzt geht es um die Überlegung, wie die Buttons implementiert werden.
Ja, die Buttons.
Ich weiß nach wie vor nur, dass es prima ist, wenn der Button seine Form entsprechend ändert, ich weiß aber nicht, ob er seine Position ändern soll. Auch finde ich es übersichtlicher, wenn ein Button für genau eine Aktion da ist. Es ist auch weniger fehleranfällig, als das Austauschen von gesendeten Nachrichten zur Laufzeit. Hinzu kommt, dass auch blinde Menschen die App nutzen können sollen.
(Ich kenne mehrere blinde Personen, die voll erwerbstätig sind und ein Smartphone benutzen. Meist ein iPhone, aber das liegt sicherlich nur daran, dass die 08-15 Entwickler unter Android Sehbehinderte einfach ignorieren.)
Entsprechend tausche ich also nicht nur Aussehen und Nachricht des Buttons, sondern auch noch seinen via Talkback ausgegebenen Hinweistext.
Das sind dann viel zu viele Änderungen, die im Nachhinein nur für Verwirrung sorgen und das ganze Programm anfällig für Flüchtigkeitsfehler machen.
Also, da ich weiß, dass ich zwei Buttons habe und auch ungefähr weiß, wie ich diese Buttons nennen werde, kann ich auch die beiden Tests fertigstellen.
@Test
public void startButtonPressed() {
ViewInteraction enterWorkButtonInteraction = onView( withId( R.id.enterWorkButton ) );
enterWorkButtonInteraction.perform( click() );
enterWorkButtonInteraction.check( matches( isDisplayed() ) );
onView( withId( R.id.leaveWorkButton ) ).check( doesNotExist() );
}
@Test
public void endButtonPressed() {
ViewInteraction leaveWorkButtonInteraction = onView( withId( R.id.leaveWorkButton ) );
leaveWorkButtonInteraction.perform( click() );
leaveWorkButtonInteraction.check( matches( isDisplayed() ) );
onView( withId( R.id.enterWorkButton ) ).check( doesNotExist() );
}
Alles anzeigen
Damit das kompiliert dürfen wir endlich ein UI erstellen. Aber wirklich nur zwei Buttons $irgendwo mit den beiden IDs vergeben.
An der Positionierung wird erst geschraubt wenn wir wissen, wie sie sein soll!
Nachdem das UI so kurz erledigt ist können wir das gewünschte zu testende Verhalten implementieren.
OnClick Listener, die einfach sich selbst sichtbar und 'den Anderen' unsichtbar machen sollten reichen.
Im zugehörigen Branch könnt ihr auch nachverfolgen, wie das Refactoring so vor sich ging.