Wie schreibe ich wartbare, nicht spröde Unit-Tests für eine GUI?


16

Ich habe versucht, UI-Komponententests für meine GUI-Apps zu schreiben, und habe das Problem, dass sie beim ersten Schreiben zwar gut funktionieren, sich jedoch als spröde herausstellen und bei jeder Änderung des Designs (das heißt, ziemlich oft) beschädigt werden. Ich habe Mühe, eine Reihe von Richtlinien zu finden, die zu wartbaren Komponententests für die GUI führen.

Im Moment habe ich festgestellt, dass Tests, die besagen, dass "diese Komponente ihre Eingabedaten irgendwo anzeigen soll", gut sind (und das ist mit HTML sehr einfach). Tests, die einen bestimmten Zustand eines bestimmten Teils des Bauteils überprüfen, sind normalerweise spröde. Tests, die wie Click-Click-Click-Expect ablaufen und versuchen, dem Benutzerverhalten und der zugrunde liegenden Geschäftslogik (was der wichtigste Teil ist) zu folgen, stellen sich normalerweise als spröde heraus. Wie schreibe ich gute Tests?


Um genauer zu sein, möchte ich einige Muster darüber kennen, was ich in meiner Benutzeroberfläche testen kann, und nicht genau, wie ich es testen soll. Namenskonventionen und feste Bezeichner sind gut, lösen jedoch nicht das Hauptproblem, nämlich dass sich die Benutzeroberflächen stark ändern. Ich möchte die Verhaltensweisen testen, deren Änderung am unwahrscheinlichsten ist. Wie finde ich das Richtige zum Testen?


1
@MichaelDurrant: Sie haben die Frage so bearbeitet, dass sie sich auf das Testen der Benutzeroberfläche im Allgemeinen bezieht, während ich ursprünglich nach Unit-Tests gefragt habe. Ich finde es schwieriger, Integrationstests aufrechtzuerhalten, und ich bevorzuge Unit-Tests, wann immer dies möglich ist.
mik01aj

2
Ich denke, dies ist ein Teil des Problems. Grundsätzlich kann man keine Schnittstelle durch Komponententests alleine testen, da das Hauptziel darin besteht, eine Schnittstelle zu etwas herzustellen. GUIs sind in dieser Hinsicht nicht anders.
jk.

m01 können Sie es wieder ändern. Ich stelle mir UI-Tests normalerweise als integrierte Tests vor. Die Tests basieren in der Regel auf den vorhandenen Seed- und Fixture-Daten und der damit arbeitenden Benutzeroberfläche. Wenn Sie echte UI-Tests haben, die sich nicht auf andere Daten stützen, sind diese ausgezeichnet. Allerdings habe ich festgestellt, dass dies relativ selten ist.
Michael Durrant

2
verwandt, aber kein Duplikat, da es sich um GUI-Tests handelt: programmers.stackexchange.com/questions/109703/…
k3b

Antworten:


3

Ein häufiges Problem bei GUI-Tests ... Der Hauptgrund, warum diese Tests als spröde eingestuft werden, besteht darin, dass sie eine Änderung der GUI, die keine Änderung der Anforderungen darstellt, nicht überstehen können . Sie sollten sich bemühen, Ihren Testcode so zu strukturieren, dass eine Änderung in der Benutzeroberfläche auf eine einzelne Stelle in den Tests beschränkt ist.

Betrachten Sie als Beispiel einen Test mit folgendem Wortlaut:

Wenn der Benutzer "999" in das Feld "Telefonnummer" eingibt und auf die Schaltfläche "Speichern" klickt, sollte im Popup der Fehlermeldung "Ungültige Telefonnummer" angezeigt werden.

Hier ist genügend Platz, damit dieser Test unterbrochen wird, wenn Sie die Schnittstelle überarbeiten, auch wenn die Validierungsanforderung weiterhin besteht.

Lassen Sie uns dies in eine kleine alternative Formulierung einfügen:

Wenn der Benutzer "999" als Telefonnummer eingibt und die Profilseite speichert, sollte der Fehler "Ungültige Telefonnummer" angezeigt werden.

Der Test ist der gleiche, die Anforderungen sind die gleichen, aber diese Art von Test wird eine Überarbeitung für Ihre Benutzeroberfläche überstehen. Sie müssen den Code natürlich ändern, aber der Code wird isoliert. Selbst wenn Sie zehn oder zwanzig solcher Tests für Ihre Profilseite haben und Ihre Validierungslogik zur Anzeige von Fehlermeldungen von Javascript-Warnungen auf beispielsweise jquery-popups umstellen, müssen Sie nur den einzelnen Testteil ändern, der Fehlermeldungen prüft.


4

Dies ist ein häufiges Problem. Ich würde auf Folgendes achten:

  • Wie Sie Elemente benennen

    Verwenden Sie die CSS-ID oder -Klasse, um Elemente zu identifizieren. Bevorzugen Sie die Verwendung der CSS-ID, wenn das Objekt eindeutig ist. Berücksichtigen Sie das von Ihnen verwendete Framework. Beispielsweise wird das nameAttribut bei Ruby on Rails automatisch zugewiesen und kann (nicht intuitiv) besser sein als die Verwendung der CSS-ID oder -Klasse

  • Wie Sie Elemente identifizieren.

    Vermeiden Sie Positionskennungen wie table/tr/td/tdzu Gunsten von Formularen wie td[id="main_vehicle"oder td[class='alternates']. Erwägen Sie gegebenenfalls die Verwendung von Datenattributen. Auch versuchen , besser zu vermeiden Layout - Tags wie <td>ganz so für die oben Sie entweder eine Spannweite und Nutzung könnten hinzufügen , dass, zum Beispiel <span id="main_vehicle">oder einen Platzhalter Wähler wie in *[id="main_vehicle"]dem den *könnte jetzt ein div sein, spannt, td, usw.

  • Mit Test spezifische Datenattributen , die nur für qa und Tests verwendet werden.

  • Vermeiden Sie unnötige Qualifikationen für Elemente. Möglicherweise verwenden Sie Folgendes:

    body.main div#vehicles > form#vehicle input#primary_vehicle_name

    Dies erfordert jedoch, dass das Eingabefeld in einem Formular mit einer genauen ID des Fahrzeugs und auf einer Seite mit einem Hauptteil mit der Klasse main und einem Hauptteil mit der ID des Fahrzeugs verbleibt, das ein unmittelbares Kind eines Formulars mit der ID von hat Fahrzeug. Jede Änderung dieser Struktur und der Test wird abgebrochen. In diesem Fall könnten Sie das finden

    input#primary_vehicle_name

    reicht aus, um das Element eindeutig zu identifizieren.

  • Vermeiden Sie Tests, die sich auf sichtbaren Text beziehen. Der Text auf der Seite, der dem Benutzer angezeigt wird, ändert sich normalerweise im Laufe der Zeit, wenn die Site gewartet und aktualisiert wird. Verwenden Sie daher Bezeichner wie CSS-ID und CSS-Klasse oder Datenattribute. Elemente wie form, inputund selectin Formen verwendet werden , sind auch gute Teile zu identifizieren Elemente, in der Regel in Kombination mit der ID oder Klasse, zB li.vehicleoder input#first-vehicle Sie können auch Ihre eigenen Kennungen hinzufügen, zum Beispiel <div data-vehicle='dodge'>. Auf diese Weise können Sie die Verwendung von Element-IDs oder -Klassen vermeiden, die wahrscheinlich von Entwicklern und Designern geändert werden. Ich habe im Laufe der Zeit herausgefunden, dass es besser ist, nur mit Entwicklern und Designern zusammenzuarbeiten und sich über Namen und Geltungsbereiche zu einigen. Es ist schwer.

  • Wie feste Daten gepflegt werden

    Versuchen Sie, ähnlich wie bei der Identifizierung tatsächlicher Elemente, zu vermeiden, dass der fest codierte Selektor Werte zugunsten von Seitenobjekten identifiziert - kleine Textteile, die in Variablen oder Methoden enthalten sind und somit wiederverwendet und auch zentral verwaltet werden können. Beispiele für Javascript-Variablen nach diesem Muster für fest codierte Werte:

    storedVars["eqv_auto_year"] = "2015";
    storedVars["eqv_auto_make_1"] = "ALFA ROMEO";
    storedVars["eqv_auto_make_2"] = "HONDA";`  
    

    Weitere Informationen zu Seitenobjekten finden Sie im Selen-Wiki und in den Selen-Dokumenten

  • Kommunikation mit Entwicklern.

    Unabhängig von der technischen Herangehensweise in Bezug auf "Entwickler nehmen Änderungen vor und brechen die QS-Automatisierung", die ein Workflow-Problem darstellt. Sie müssen sicherstellen, dass: jeder ein und dasselbe Team ist; Entwickler führen dieselben integrierten Tests durch; Standards werden von beiden Gruppen vereinbart und befolgt; Die Definition von "erledigt" umfasst das Ausführen und möglicherweise Aktualisieren der UI-Tests. Entwickler und Tester arbeiten gemeinsam an Testplänen und beide nehmen an der Ticketbereinigung teil (wenn sie Agile ausführen) und sprechen im Rahmen der Bereinigung über das Testen der Benutzeroberfläche. Sie sollten sicherstellen, dass der Ansatz und die Strategie, die Sie für die Benennung verwenden, mit den Anwendungsentwicklern abgestimmt sind. Wenn Sie nicht auf der gleichen Seite sind, wird es Ihnen gefallen, über die Benennung von Objekten zu streiten. Einige Beispiele für Seitenobjektmethoden, die ich kürzlich für ein Ruby-Projekt erstellt habe:

    def css_email_opt_in_true
      'auto_policy[email_opt_in][value=1]'
    end 
    
    def css_phone_opt_in
      '*[name="auto_policy[phone_opt_in]"]'
    end 
    
    def css_phone_opt_in_true
      'input[name=phone_opt_in][value=true]'
    end 
    
    def css_credit_rating
      'auto_policy[credit_rating]'
    end
    

    Hier sind die gleichen Seitenobjekte wie bei Javascript-Variablen:

    storedVars["css_email_opt_in"] = "css=*[name='auto_policy[email_opt_in]']";
    storedVars["css_phone_opt_in"]="css=*[name='auto_policy[phone_opt_in]']";
    storedVars["css_phone_opt_in_true"]="css=input[name='phone_opt_in'][value=true]";
    storedVars["css_credit_rating"]="css=select[name='auto_policy[credit_rating]']";
    

2
Dies sind nützliche Hinweise, und die meisten befolge ich bereits. Mein Problem war, dass ich Tests für eine Schaltfläche schreibe und diese Schaltfläche dann entfernt wird, während dieselbe Aktion von einer anderen Stelle ausgeführt wird. Oder die Schaltfläche bleibt dort, aber die Beschriftung ändert sich und die Schaltfläche führt auch eine zusätzliche Aktion aus.
mik01aj

1
Ahh, das ist gut zu wissen. Ja, für dieses Zeug würde ich mich auf den Workflow und die organisatorische QA-Entwickler-Interaktion konzentrieren
Michael Durrant

1
Ich poste auch Informationen für die anderen, die Ihre Frage finden und möglicherweise nicht alle Teile an Ort und Stelle haben, die Sie haben oder die Sie möglicherweise gar nicht kennen.
Michael Durrant

1
Ich habe meinen letzten Bulllet-Punkt basierend auf Ihrem Feedback erweitert.
Michael Durrant

1
Und ich habe die Teile über das Benennen und Identifizieren von Elementen und über das Nichtverwenden von sichtbarem Text aktualisiert.
Michael Durrant

3

Der Grund, warum Leute zuerst Dinge wie MVC, MVP und Presenter und ähnliche Designmuster entwickelten, bestand darin, die Geschäftslogik von der Benutzeroberfläche zu trennen.

Offensichtlich kann der Ansichtsteil nur getestet werden, indem das Programm gestartet und überprüft wird, was es anzeigt. Mit anderen Worten, er kann nur in Abnahmetests getestet werden.

Zum anderen kann das Testen der Geschäftslogik in Unit-Tests durchgeführt werden. Und das ist die Antwort auf Ihre Frage. Testen Sie alles im Modell, und wenn Sie können und wollen, können Sie auch den Code des Controllers testen.


GUIs ändern sich sehr

Das kann nur passieren, wenn sich die Anforderungen ändern. Wenn sich eine Anforderung ändert, führt kein Weg daran vorbei, außer den Code zu ändern. Wenn es Ihnen gelingt, ein gutes Design und eine gute Architektur zu erstellen, werden die Änderungen an vielen Stellen nicht verbreitet.


2
Ich denke, das ist ein guter Punkt. Vielleicht mache ich MVC einfach falsch :) Übrigens glaube ich, dass sich ändernde Anforderungen unvermeidlich sind, wenn Sie eine Webplattform für viele Benutzer entwickeln. Sie wissen nicht, wie sich Ihre Benutzer verhalten, bis sie die GUI verwenden.
mik01aj

2

GUI-Interaktionstests sollten nicht mehr oder weniger spröde sein als alle anderen Arten von Tests. Das ist; Wenn sich Ihre Anwendung auf irgendeine Weise ändert, müssen die Tests aktualisiert werden, um dies widerzuspiegeln.

Zum Vergleich:

Gerätetest

Original : validateEmail()sollte eine InvalidDataAusnahme auslösen. Welches ist richtig in Ihrem Unit-Test abgedeckt.

Ändern : validateEmail()sollte eine InvalidEmailAusnahme auslösen. Jetzt ist dein Test falsch, du aktualisierst ihn und alles ist wieder grün.

GUI-Test

Original : Die Eingabe einer ungültigen E-Mail führt zu einer Popup-Fehlermeldung mit dem Hinweis "Ungültige Daten eingegeben". Richtig erkannt durch Ihre Tests.

Änderung : Die Eingabe einer ungültigen E-Mail führt zu einem Inline-Fehler mit dem Hinweis "Ungültige E-Mail eingegeben". Jetzt ist dein Test falsch, du aktualisierst ihn und alles ist wieder grün.


Denken Sie daran, dass Sie auf Ein- und Ausgänge testen - ein klar definiertes Verhalten. Unabhängig davon, ob es sich um einen GUI-Test, einen Unit-Test oder einen Integrationstest usw. handelt.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.