Ein großer Teil der Arbeit eines Qualitätsprüfers besteht in der Fehlerlokalisierung.
Natürlich helfen uns Testdesigntechniken bei der Auswahl von Testszenarien und machen die Dinge effizienter. Aber was genau ist Fehlerlokalisierung und wie können wir sie weniger schmerzhaft gestalten?
Lokalisierung ist wie Detektiv spielen: „Wo und wann ist etwas schiefgelaufen?“ Ohne ordnungsgemäße Lokalisierung kann ein Defekt zu einem heißen Eisen werden, das zwischen Frontend, Backend und jedem Entwicklungsteam hin- und hergeschoben wird. Zeit geht verloren und möglicherweise sogar der Kontext.
Stellen Sie sich die Fehlerlokalisierung als das Navigieren durch ein Labyrinth vor, wobei Anwendungsanforderungen und Protokolle Ihr Wollknäuel sind. Aber wäre es nicht einfacher, eine Karte dieses Labyrinths zu haben, selbst eine grobe, als einfach mit Wollknäuel herumzustolpern? Hier kommt die Architektur der Anwendung ins Spiel.
Es geht darum, wie die verschiedenen Teile des Systems zusammenarbeiten. Um es mit unserer Labyrinth-Metapher auszudrücken: Es geht darum, wie ein Abschnitt mit einem anderen verbunden ist, welche Durchgänge wohin führen.
Ich unterscheide zwei Hauptarchitekturen: Client-Server und Backend.
Es gibt im Allgemeinen zwei Typen:
Der Typ bestimmt, wie viele Informationen der Client speichert und selbst verarbeitet. Es gibt andere Möglichkeiten, dies einzurichten, aber ich bleibe bei dem, womit ich tatsächlich gearbeitet habe.
Die meisten mobilen und Web-Apps sind Thin Clients. Alle Informationen werden auf dem Server gespeichert und die Client-Anwendung fordert Daten an oder verlangt deren Verarbeitung. Registrieren, Anmelden, Abonnieren von Benachrichtigungen – all dies sind Aufrufe an den Server. Die gesamte Verarbeitung auf dem Server ist für den Client verborgen. Als Antwort auf die Anfrage erhält der Client gesammelte und verarbeitete Informationen aus der Datenbank oder eine Bestätigung, dass die Anfrage erfolgreich abgeschlossen wurde.
Bei Thick-Client-Anwendungen führt der Client den Großteil der Verarbeitung selbst durch: Daten zur Datenbank hinzufügen, Berichte erstellen, Summen berechnen und Dokumente erstellen. Sie werden oft lokal installiert, aber nicht immer. Beispiele für Thick Clients sind Offline-Spiele, AutoCAD und einige Versionen von 1C.
Zwei gängige Ansätze sind:
Wenn fast alles an einem Ort verarbeitet wird, handelt es sich um einen Monolithen.
Wenn Anfragen zur Verarbeitung an andere Dienste innerhalb des Systems gesendet werden, handelt es sich wahrscheinlich um eine Microservices-Architektur.
In einer monolithischen Architektur kann es schwierig sein, die Quelle eines Defekts genau zu bestimmen, da verschiedene Teams und Dienste normalerweise dieselbe Codebasis verwenden, was bedeutet, dass Änderungen unerwartete Folgen haben können.
Im zweiten Fall werden die Dienste getrennt und verfügen jeweils über eine eigene Codebasis. Dies bedeutet, dass Änderungen an einem Dienst kaum Auswirkungen auf andere haben.
Der Titel klingt beängstigend, aber er sagt Ihnen nur, wer was tut und wer für welchen Teil des Labyrinths (der Anwendung) verantwortlich ist. Stellen Sie sich vor, wir haben ein großes Unternehmen: eine Bank, einen Marktplatz, einen Essenslieferdienst – was auch immer. Je größer und komplexer unsere Anwendung ist, desto mehr Leute arbeiten daran. Und je mehr Leute es gibt, desto mehr müssen Sie sie in Teams aufteilen, von denen jedes für seinen eigenen Entwicklungsbereich verantwortlich ist.
Beispielsweise kann ein Team für Werbeaktionen zuständig sein, während ein anderes für Zahlungen verantwortlich ist. Wenn unsere Anwendung unterschiedliche Dienste anbietet, können Teams für einzelne Dienste verantwortlich sein, wie etwa elektronische Dokumentenverwaltung, Buchhaltung oder öffentliche Beschaffung.
Sie müssen nicht alles und jeden wissen, aber wenn es eine Dokumentation gibt, die beschreibt, welches Team für welchen Bereich verantwortlich ist, bewahren Sie diese am besten mit einem Lesezeichen auf.
Mit der Karte in der Hand und dem Garn bereit, tauchen wir in unser Labyrinth ein und suchen nach der Quelle eines Defekts. Stellen wir uns ein paar Szenarien vor.
Stellen Sie sich vor: Wir testen eine Website für einen Konversationsclub.
Wir blättern durch den Stundenplan und lesen Informationen zu den kommenden Sitzungen, als uns irgendwann ein Tippfehler auffällt.
Wie finden wir nun heraus, wo es seinen Ursprung hat? Das Abenteuer kann beginnen!
Wir öffnen devTools, aktualisieren die Seite und sehen uns die Anfragen und Antworten an. Da wir einen Thin Client haben, finden wir unseren Tippfehler in einer der Antworten – er kam aus dem Backend.
Jetzt öffnen wir die Protokolle und suchen nach der Verarbeitung der Anfrage oder Antwort des Backends – das ist unser Garn aus dem Zauberball. Wir können die Protokolle mit beliebigen Informationen aus der Anfrage und Antwort durchsuchen, aber es ist besser, eindeutige Werte zu verwenden: Anfrage-XIID, ID aus der Anfrage, Telefonnummer usw.
Wir finden den Eintrag und prüfen: Haben wir die Klasseninformationen aus der Datenbank oder von einem anderen Dienst?
Wenn die Informationen aus der Datenbank stammen, können wir das Problem an den technischen Support weiterleiten, um den Tippfehler in der Datenbank zu beheben.
Stammen die Informationen von einem anderen Dienst, können wir den Mangel an diesen weitergeben.
Herzlichen Glückwunsch! Wir haben unser erstes Labyrinth bewältigt: Der Defekt ist lokalisiert und gemeldet.
Stellen Sie sich nun vor, wir testen ein Registrierungsformular.
Wir geben eine E-Mail, einige Daten und ein erfundenes Passwort ein. Wir klicken auf die Schaltfläche „Registrieren“ und erhalten unerwartet eine Fehlermeldung.
Es ist Zeit, unseren Zauberball zu entwirren! Wir gehen zu unserer beliebten Registerkarte „Netzwerk“ in devTools und sehen, was schiefgelaufen ist: Wir wiederholen alle Schritte und überprüfen die Antwort des Servers.
Als Antwort auf die Anfrage erhalten wir einen 400-Code mit einem leeren Antworttext. Sollten wir loslaufen und einen Defekt beim Frontend melden? Aber wir wissen immer noch nicht, was genau schiefgelaufen ist und was behoben werden muss. Oft tritt ein 400-Fehler auf, wenn es eine Diskrepanz zwischen dem gibt, was der Client gesendet hat, und dem, was der Server erwartet hat. Dafür kann es viele Gründe geben, darunter:
Lassen Sie uns die Anfrage des Kunden prüfen
Wenn wir über manuell verfasste oder in Swagger oder OpenAPI erstellte Dokumentation verfügen, verwenden wir diese, um Folgendes zu überprüfen:
Wie können wir die Anfrage sonst noch prüfen?
Auch wenn wir keine Unterlagen haben, können wir Folgendes überprüfen:
Alles in Ordnung? Dann geht es weiter durch das Labyrinth auf der Suche nach der Antwort. Wir nehmen unsere Karte und „steigen“ in die Baumstämme hinab.
Protokollanalyse
Hierbei sind zwei Szenarien möglich:
Im letzteren Fall müssen wir unsere Reise durch das Microservice-Labyrinth fortsetzen und nach der Stelle suchen, an der unsere Anfrage verarbeitet wurde.
Sobald wir das Fehlerprotokoll gefunden haben, wissen wir, was genau schiefgelaufen ist. Das bedeutet, dass unsere Lokalisierung und unsere Reise abgeschlossen sind! Jetzt müssen wir nur noch die folgenden Informationen für den Fehlerbericht sammeln:
Die Fehlerlokalisierung kann eine Herausforderung sein. Manchmal stößt man auf eine Mauer: Das Protokoll, dem man gefolgt ist, führt nicht zum Fehler oder macht die Dinge noch verwirrender. In solchen Situationen gehe ich normalerweise ein paar Schritte zurück oder beginne von vorne.
Das Erkunden des Labyrinths kann viel Zeit in Anspruch nehmen. Die Reise kann schwierig und gefährlich sein: Die Verarbeitung einiger Anfragen kann kompliziert sein und Anfragen an mehrere verschiedene Dienste senden. Manchmal ist es sinnvoll, die Aufgabe zu vereinfachen und die Architekten des Labyrinths – die Entwickler – zu kontaktieren.