Genau das ist in meinem Projekt passiert, und heute werde ich Ihnen sagen, wie Sie die Anzahl der Testfälle weiter reduzieren können, ohne an Qualität zu verlieren.
Testobjekt
Zuerst erzähle ich Ihnen ein wenig über das Produkt. Bei Tinkoff hat unser Team Blöcke entwickelt - dies sind React-Komponenten, die aus Implementierung und Konfiguration bestehen. Die Implementierung ist die Komponente selbst, die wir entwickelt haben und die der Benutzer im Browser sieht. Die Konfiguration ist JSON, das die Parameter und den Inhalt dieses Objekts festlegt.
Die Hauptaufgabe der Blöcke ist es, schön zu sein und für verschiedene Benutzer gleich zu sein. Gleichzeitig kann sich der Block gegenüber der Konfiguration und dem Inhalt erheblich ändern.
Zum Beispiel kann ein Block so sein - ohne Hintergrund, mit einer Schaltfläche und einem Bild rechts:
Oder so - mit einem Hintergrund, ohne Schaltfläche und mit einem Bild links:
Oder im Allgemeinen so - mit einem Link anstelle einer Schaltfläche und ohne Liste im Text:
Alle obigen Beispiele sind derselbe Block, der eine Version der Konfiguration enthält (eine JSON-Struktur, die diese bestimmte React-Komponente verarbeiten kann), aber ihren unterschiedlichen Inhalt.
Die Schaltung selbst:
{
components: {
background: color,
panel: {
panelProps: {
color: {
style: ['outline', 'color', 'shadow', 'custom'],
background: color
},
size: ['s', 'm', 'l'],
imagePosition: ['left', 'right']
},
title: {
text: text,
size: ['s', 'l'],
htmlTag: ['div', 'b', 'strong', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']
},
description: {
text: html,
htmlTag: ['div', 'b', 'strong', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']
},
image: {
alt: text,
title: text,
image: {
src: image,
srcset: [{
src: image,
condition: ['2x', '3x']
}],
webpSrcset: [{
src: image,
condition: ['1x', '2x', '3x']
}]
},
imageAlign: ['top', 'center', 'bottom']
},
button: {
active: boolean,
text: text,
color: {
style: ['primary', 'secondary', 'outline', 'outlineDark', 'outlineLight', 'textLink', 'custom'],
backgroundColor: color
},
onClick: {
action: ['goToLink', 'goToBlock', 'showBlock', 'crossSale', 'callFormEvent'],
nofollow: boolean,
url: url,
targetBlank: boolean,
title: text,
noindex: boolean,
guid: guid,
guidList: [{
guid: guid
}],
formId: guid,
crossSaleUrl: url,
eventName: text
},
htmlTag: ['div', 'b', 'strong', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']
},
href: url
}
}
}
In diesem Fall hat der Block mit dem Bild rechts einen Wert
components.panel.imagePosition = right. Und der Block mit dem Bild links hat components.panel.imagePosition = left. Für einen Block mit einem Knopf - components.button.active = trueund so weiter. Ich hoffe das Prinzip ist klar. So werden alle Parameter des Blocks eingestellt.
Fälle aus einer Kombination von Parametern
In diesem Artikel werde ich nicht auf die Probleme der Blockdiagrammversionierung, der Regeln zum Füllen von Inhalten oder der Herkunft der Daten eingehen. All dies sind separate Themen, die sich nicht auf die Zusammenstellung einer Reihe von Testfällen auswirken. Das Wichtigste zu wissen: Wir haben viele Parameter, die sich auf unsere Komponente auswirken, und jeder von ihnen kann seine eigenen Werte annehmen.
Für das obige Beispiel habe ich einen Block mit einer ziemlich einfachen Konfiguration ausgewählt. Aber selbst darin wird das Überprüfen aller Wertekombinationen für alle Parameter unannehmbar lange dauern, insbesondere wenn Sie die browserübergreifende Kompatibilität berücksichtigen müssen. Normalerweise helfen hier paarweise Tests oder paarweise Tests. Es wurden bereits Tonnen von Artikeln über ihn geschrieben und es gibt sogar Schulungen . Wenn Sie plötzlich nicht mehr auftauchen, lesen Sie unbedingt.
Lassen Sie uns abschätzen, wie viele Testfälle wir bei der Anwendung erhalten. Wir haben mehr als 25 Parameter, und einige von ihnen nehmen bis zu 7 und 9 Varianten von Werten an. Ja, Sie können etwas vernachlässigen: Wenn Sie beispielsweise das Layout überprüfen, ist die Anleitung für Sie nicht wichtig. Mit Pairwise Testing erhalten Sie jedoch immer noch mehr als 80 Testfälle. Und dies ist, wie ich bereits schrieb, nicht der komplexeste Block und ohne Berücksichtigung der Cross-Browser-Kompatibilität. Wir haben jetzt mehr als 150 Blöcke und ihre Anzahl wächst. Daher können wir uns nicht so viele Fälle leisten, wenn wir die Geschwindigkeit des Testens und der Veröffentlichung neuer Versionen beibehalten möchten.
Fälle von einem Parameter
Die paarweise Testmethode basiert auf der Aussage, dass die meisten Fehler durch das Zusammenspiel von nicht mehr als zwei Faktoren verursacht werden. Das heißt, die meisten Fehler treten entweder bei einem Wert eines Parameters oder bei einer Kombination der Werte zweier Parameter auf. Wir haben beschlossen, den zweiten Teil dieser Anweisung zu ignorieren und gingen davon aus, dass bei der Überprüfung eines Parameters immer noch die meisten Fehler gefunden werden.
Dann stellt sich heraus, dass wir zum Testen jeden Wert jedes Parameters mindestens einmal überprüfen müssen. Gleichzeitig trägt jeder Block die gesamte Konfiguration. Anschließend können Sie in jedem neuen Fall das Maximum der noch nicht verifizierten Werte überprüfen, um die Anzahl der Fälle zu minimieren.
Lassen Sie uns den Algorithmus zum Erstellen von Fällen anhand eines vereinfachten Beispiels analysieren. Nehmen wir die Schaltflächenkomponente aus unserem Schema und erstellen Testfälle dafür:
button: {
active: boolean,
text: text,
color: {
style: ['primary', 'secondary', 'outline', 'custom'],
backgroundColor: color
}
Um das Beispiel zu vereinfachen, habe ich die Länge der Liste auf reduziert
button.color.style.
Schritt 1. Erstellen Sie Inhaltsoptionen für jedes Feld
Hier ist alles wie beim paarweisen Testen: Sie müssen verstehen, welche Werte jedes Feld annehmen kann. Zum Beispiel
button.activein unserem Fall kann es nur zwei Werte annehmen: trueoder false. Theoretisch können sich weitere Optionen ergeben, beispielsweise das undefinedFehlen des Schlüssels.
Hier ist es meiner Meinung nach wichtig, die Grenzen und Funktionen Ihres Systems sehr klar zu definieren und keine unnötigen Dinge zu überprüfen. Das heißt, wenn die Überprüfung auf obligatorische Schlüssel oder die Validierung eines Werts in einem Drittanbieter-System implementiert ist, muss diese Funktionalität in einem Drittanbieter-System überprüft werden. Und wir sollten nur "richtige" Daten als Fälle verwenden.
Im Großen und Ganzen wird das gleiche Prinzip in der Testpyramide angewendet. Auf Wunsch können uns die wichtigsten Integrationstests hinzugefügt werden, um beispielsweise die Verarbeitung eines nicht eingetroffenen Schlüssels zu überprüfen. Es sollte jedoch eine Mindestanzahl solcher Tests geben. Ein anderer Ansatz ist das Streben nach umfassenden Tests, die bekanntlich unmöglich sind.
Daher haben wir die Inhaltsoptionen für jedes Feld identifiziert und die folgende Tabelle erstellt:
Diese Tabelle enthält jede Äquivalenzklasse jedes Parameters, jedoch nur einmal.
Dies sind die Wertklassen in unserem Fall, die Klassen:
- text_s - kurze Zeichenfolge;
- text_m - längerer String;
- no_color - keine Farbe;
- rnd_color ist eine beliebige Farbe.
Schritt 2. Anreichern der Tabelle mit Daten
Da jeder Block die vollständige Konfiguration enthält, müssen wir den leeren Zellen einige relevante Daten hinzufügen:
Jetzt ist jede Spalte ein Fall.
Da wir die fehlenden Daten selbst auswählen, können wir gleichzeitig Fälle basierend auf der Priorität generieren. Wenn wir beispielsweise wissen, dass Kurztext in einer Schaltfläche häufiger verwendet wird als Text mittlerer Länge, lohnt es sich, ihn häufiger zu überprüfen.
Im obigen Beispiel können Sie auch auf die "gelöschten" Fälle achten - Fälle, in denen einige Parameter überhaupt nicht überprüft werden, obwohl sie in der Tabelle vorhanden sind. In diesem Fall
button.color.style: secondarywird das Erscheinungsbild nicht überprüft, da es keine Rolle spielt, welchen Stil die deaktivierte Schaltfläche hat.
Um zu verhindern, dass die "gelöschten" Fälle zu Fehlern führen, haben wir die resultierenden Wertesätze analysiert. Die Analyse wurde einmal beim Generieren von Testfällen durchgeführt, und alle "abgelegten" Fälle wurden manuell zum endgültigen Testfall hinzugefügt. Eine solche Lösung des Problems ist ziemlich umständlich, aber billig (es sei denn, Sie ändern natürlich selten die Konfiguration der getesteten Objekte).
Eine allgemeinere Lösung besteht darin, alle Werte in zwei Gruppen aufzuteilen:
- unsichere Werte (solche, die zum "Verlust" von Fällen führen können);
- sicher (was nicht zum "Herausfallen" führen kann).
Jeder unsichere Wert wird in einem eigenen Testfall überprüft. Sie können den Fall mit beliebigen sicheren Daten anreichern. Für sichere Werte wird eine Tabelle gemäß den obigen Anweisungen erstellt.
Schritt 3. Klären der Werte
Jetzt müssen nur noch konkrete Werte anstelle von Äquivalenzklassen generiert werden.
Hier muss jedes Projekt seine eigenen Wertevarianten auswählen, basierend auf den Eigenschaften des getesteten Objekts. Einige Werte sind sehr einfach zu generieren. Beispielsweise können Sie für die meisten Felder einfach eine beliebige Farbe verwenden. Bei einigen Blöcken müssen Sie beim Überprüfen der Farbe einen Farbverlauf hinzufügen, der jedoch in eine separate Äquivalenzklasse verschoben wird.
Mit Text ist es etwas komplizierter: Wenn Sie eine Zeichenfolge aus zufälligen Zeichen generieren, werden Silbentrennungen, Listen, Tags und nicht unterbrechende Leerzeichen nicht getestet. Wir generieren kurze und mittlere Zeichenfolgen aus echtem Text und reduzieren ihn auf die gewünschte Anzahl von Zeichen. Und im Langtext prüfen wir:
- HTML-Tag (beliebig);
- Verknüpfung;
- nicht nummerierte Liste.
Diese Reihe von Fällen ergibt sich direkt aus unserer Blockimplementierung. Beispielsweise sind alle HTML-Tags miteinander verbunden, sodass es keinen Sinn macht, jedes einzelne zu testen. In diesem Fall werden der Link und die Liste separat überprüft, da sie eine separate visuelle Verarbeitung haben (Hervorhebung bei Hover und Shootouts).
Es stellt sich heraus, dass Sie für jedes Projekt Ihren eigenen tatsächlichen Inhaltssatz basierend auf der Implementierung des getesteten Objekts erstellen müssen.
Algorithmus
Auf den ersten Blick scheint der Algorithmus natürlich komplex und die Mühe nicht wert zu sein. Wenn Sie jedoch alle Details und Ausnahmen weglassen, die ich in den obigen Absätzen zu beschreiben versucht habe, stellt sich dies ganz einfach heraus.
Schritt 1. Fügen Sie der Parametertabelle alle möglichen Werte hinzu:
Schritt 2. Duplizieren Sie Werte in leere Zellen:
Schritt 3. Verwandeln Sie abstrakte Werte in konkrete und erhalten Sie Fälle:
Jede Spalte der Tabelle ist ein Fall.
Vorteile des Ansatzes
Diese Methode zur Erzeugung von Testfällen hat mehrere wichtige Vorteile.
Weniger Fälle
In erster Linie gibt es deutlich weniger Fälle als bei paarweisen Tests. Wenn wir ein vereinfachtes Beispiel mit einem Knopf nehmen, erhalten wir 4 statt 8 Fälle in paarweisen Tests.
Je mehr Parameter sich im getesteten Objekt befinden, desto bedeutender sind die Einsparungen bei den Fällen. Zum Beispiel erhalten wir für den am Anfang des Artikels vorgestellten vollständigen Block 11 Fälle und mit Hilfe von paarweise - 260.
Die Anzahl der Fälle erhöht sich nicht mit der Komplikation der Funktionalität
Das zweite Plus ist, dass die Anzahl der Fälle nicht immer zunimmt, wenn beim Testen neue Parameter berücksichtigt werden.
Angenommen, ein Parameter
button.color.textColormit den Wertäquivalenzklassen no_colorwird unserer Schaltfläche hinzugefügt rnd_color. Dann bleiben 4 Fälle übrig, zu jedem wird nur ein weiterer Parameter hinzugefügt: Die
Anzahl der Fälle erhöht sich nur, wenn ein Parameter mehr Werte hat als es Fälle gab.
Sie können das Wichtige öfter überprüfen
Durch Anreicherung der Werte (Schritt 2 im Algorithmus) können Werte mit höherer Priorität oder mit höherem Risiko häufiger überprüft werden.
Wenn wir beispielsweise wissen, dass frühere Benutzer häufiger kürzeren Text verwendeten und jetzt längeren Text verwenden, können wir Fälle mit längerem Text anreichern und häufiger auf reale Benutzerfälle eingehen.
Kann automatisiert werden
Der obige Algorithmus ist für die Automatisierung durchaus zugänglich. Natürlich sehen die vom Algorithmus erzeugten Fälle weniger wie echte als von Menschen erzeugte aus. Zumindest durch passende Farben und zugeschnittenen Text.
Andererseits treten bereits im Entwicklungsprozess ohne Beteiligung eines Testers Fälle auf, die die Rückkopplungsschleife erheblich reduzieren.
Nachteile
Natürlich ist eine solche Fallgenerierung alles andere als eine Silberkugel und hat ihre Nachteile.
Schwierigkeiten bei der Analyse des Ergebnisses
Ich denke, Sie haben bemerkt, dass bei der Erstellung von Fällen Testdaten miteinander gemischt werden. Aus diesem Grund wird es schwieriger, die Ursache des Sturzes zu identifizieren, wenn der Fall fällt. Schließlich haben einige der im Fall verwendeten Parameter keinerlei Einfluss auf das Testergebnis.
Dies macht es einerseits wirklich schwierig, die Testergebnisse zu analysieren. Wenn das zu testende Objekt jedoch eine große Anzahl erforderlicher Parameter erfordert, ist es auch schwierig, die Ursache des Fehlers zu finden.
Fehler können übersehen werden
Zurück zum Anfang des Artikels: Wenn Sie diese Methode verwenden, können Sie Fehler überspringen, die durch eine Kombination von zwei oder mehr Parametern verursacht werden. Aber wir gewinnen an Geschwindigkeit, und es liegt an Ihnen, zu entscheiden, was für jedes spezifische Projekt wichtiger ist.
Um Fehler nicht zweimal zu verpassen, haben wir die Zero Bug Policy eingeführt und begonnen, jeden fehlenden Fehler mit einem zusätzlichen Testfall zu schließen - nicht mehr automatisch generiert, sondern von Hand geschrieben. Dies ergab hervorragende Ergebnisse: Jetzt haben wir über 150 Blöcke (getestete Objekte), mehrere Releases pro Tag und 0 bis 3 verpasste unkritische Fehler pro Monat.
Schlussfolgerungen
Wenn Ihr getestetes Objekt über eine Vielzahl von Eingabeparametern verfügt und Sie versuchen möchten, die Anzahl der Fälle und damit die Testzeit zu verringern, empfehle ich, die oben beschriebene Methode zum Generieren von Fällen mit einem Parameter zu verwenden.
Meiner Meinung nach ist es ideal für Front-End-Komponenten: Sie können die Zeit um mehr als das Dreifache reduzieren, um beispielsweise das Erscheinungsbild durch Screenshot-Tests zu überprüfen. Und die Entwicklung wird schneller ablaufen, da Fälle in den frühesten Stadien auftreten.
Wenn Sie den Autopiloten des neuen Tesla testen, können Sie natürlich auch die geringe Wahrscheinlichkeit, einen Fehler zu verpassen, nicht vernachlässigen. Vergessen Sie jedoch in den meisten Fällen nicht, dass Geschwindigkeit in der modernen Welt ein sehr wichtiges Qualitätskriterium ist. Und die Geschwindigkeitssteigerung führt zu positiveren Ergebnissen als ein paar kleinere Probleme.
Und für die Verantwortlichsten werde ich Ihnen im nächsten Artikel erklären, wie Sie sich zusätzlich vor kniffligen Fehlern schützen können, die durch eine Kombination von Parametern mithilfe von benutzerdefinierten Fällen und StoryBook verursacht werden.