Testen Sie Ihr JavaScript mit Jasmin

Wir alle wissen, dass wir unseren Code testen sollten, tun dies aber nicht. Ich denke, es ist fair zu sagen, dass die meisten von uns das abschrecken, weil es neun von zehn bedeutet, ein weiteres Konzept zu lernen. In diesem Tutorial werde ich Ihnen ein tolles kleines Framework für das einfache Testen Ihres JavaScript-Codes vorstellen.

Wussten Sie übrigens, dass Sie Ihre JavaScript-Fehler schnell und einfach von einem Experten von Envato Studio beheben lassen können?

ThemeManiac beispielsweise behebtJavaScript-Fehler oder Probleme mit der Browser-Kompatibilität auf Ihrer Website oder Webanwendung. Die Korrekturen können aufgrund der Komplexität und der verfügbaren Informationen sehr schnell durchgeführt werden. Er kann auch Ihre Skripte reorganisieren und eine völlig neue Benutzererfahrung machen. Er hat mehr als 1.000 Jobs bei Envato Studio abgeschlossen, wobei 99% der Kunden ihn empfehlen.


Schritt 0: BDD verstehen

Heute lernen wir etwas über das Jasmine BDD-Test-Framework. Wir machen aber erst einen Abstecher hier, um kurz über BDD und TDD zu sprechen. Wenn Sie mit diesen Akronymen nicht vertraut sind, stehen sie für Verhaltensorientierte Entwicklung und Testgetriebene Entwicklung. Ich bin gerade dabei zu lernen, was jedes davon in der Praxis ist und wie sie sich unterscheiden, aber hier sind einige grundlegende Unterschiede:

BDD und TDD? stehen für Verhaltensorientierte Entwicklung und Testgetriebene Entwicklung.

TDD in seiner einfachsten Form ist genau das:

  1. Schreiben Sie Ihre Tests
  2. Sieh zu, wie sie versagen
  3. Lass sie durchgehen
  4. Refactor
  5. Wiederholen

Das ist ziemlich leicht zu verstehen, eh?

BDD ist etwas komplexer: Wenn ich es jetzt verstehe, glaube ich nicht, dass Sie oder ich als einzelner Entwickler es tatsächlich vollständig ausüben können; Es ist eher eine Teamsache. Hier sind einige der Praktiken von BDD:

  • Festlegung der Ziele verschiedener Stakeholder, die für die Umsetzung einer Vision erforderlich sind
  • Einbindung der Stakeholder in den Implementierungsprozess durch Outside-In-Softwareentwicklung
  • Verwenden von Beispielen zur Beschreibung des Verhaltens der Anwendung oder von Codeeinheiten
  • Automatisieren Sie diese Beispiele, um schnelles Feedback und Regressionstests bereitzustellen

Um mehr zu erfahren, können Sie den umfangreichen Wikipedia-Artikel lesen (aus dem diese Punkte entnommen wurden)..

All dies, um zu sagen, dass Jasmine sich zwar als BDD-Framework versteht, aber eher TDD-artig verwendet wird. Das heißt aber nicht, dass wir es falsch machen. Sobald wir fertig sind, können Sie Ihr JavaScript einfach testen. und ich erwarte, dass Sie es tun!


Schritt 1: Erlernen der Syntax

Jasmine nimmt viele Hinweise von Rspec.

Wenn Sie überhaupt mit Rspec vertraut sind, können Sie die de facto BDD-Framework, Sie werden sehen, dass Jasmine viele Hinweise von Rspec erhält. Jasmin-Tests bestehen hauptsächlich aus zwei Teilen: beschreiben Blöcke und es Blöcke. Mal sehen, wie das funktioniert.

In einigen werden wir uns einige realitätsnahe Tests ansehen, aber für den Moment halten wir es einfach:

beschreiben ('JavaScript-Additionsoperator'), function () it ('addiert zwei Zahlen zusammen'), function () expect (1 + 2) .toEqual (3);););

Beide beschreiben und es Funktionen nehmen zwei Parameter an: eine Textzeichenfolge und eine Funktion. Die meisten Test-Frameworks versuchen so gut wie Englisch zu lesen, und Sie können dies mit Jasmine sehen. Beachten Sie zunächst, dass der String an übergeben wurde beschreiben und die Zeichenfolge ging an es bilden einen Satz (von Art):? Der JavaScript-Additionsoperator fügt zwei Zahlen zusammen. Dann zeigen wir Ihnen wie.

Darin drin es Block können Sie den gesamten Setup-Code schreiben, den Sie für den Test benötigen. Wir brauchen keine für dieses einfache Beispiel. Wenn Sie bereit sind, den eigentlichen Testcode zu schreiben, beginnen Sie mit dem erwarten von Funktion, weitergeben, was immer Sie testen. Beachten Sie auch, wie dies einen Satz bildet: Wir erwarten, dass 1 + 2 gleich 3 ist.?

Aber ich komme weiter. Wie ich schon sagte, egal welchen Wert du hast erwarten von wird getestet werden. Die von Ihnen aufgerufene Methode setzt den Rückgabewert von ab erwarten von, wird bestimmt, welcher Test ausgeführt wird. Diese Gruppe von Methoden wird "Matcher" genannt, und wir werden uns heute einige davon ansehen. In diesem Fall verwenden wir die toEqual matcher, der überprüft, ob der Wert übergeben wurde erwarten von und der Wert übergeben an toEqual sind den gleichen Wert.

Ich denke, dass Sie bereit sind, dies auf die nächste Ebene zu bringen, also lassen Sie uns ein einfaches Projekt mit Jasmine einrichten.


Schritt 2: Ein Projekt einrichten

Jasmin kann alleine verwendet werden; oder Sie können es in ein Rails-Projekt integrieren. Wir machen das erstere. Während Jasmine außerhalb des Browsers laufen kann (z. B. Node), können wir mit dem Download eine wirklich nette kleine Vorlage erhalten.

Gehen Sie also zur Standalone-Downloadseite und rufen Sie die neueste Version auf. Sie sollten so etwas bekommen:

Die eigentlichen Jasmine-Framework-Dateien finden Sie im lib Mappe. Wenn Sie es vorziehen, Ihre Projekte anders zu strukturieren, tun Sie dies bitte. aber wir werden das jetzt behalten.

In dieser Projektvorlage gibt es tatsächlich einige Beispielcodes. Die? Tatsächliche? JavaScript (der Code, den wir testen möchten) finden Sie im src Unterverzeichnis; wir werden unsere in Kürze dort stellen. Der Testcode-der specs-geh in die spez Mappe. Mach dir keine Sorgen über die SpecHelper.js Datei gerade noch; Wir werden darauf zurückkommen.

Das SpecRunner.html Die Datei führt die Tests in einem Browser aus. Öffnen Sie es (und aktivieren Sie das Kontrollkästchen "Bestanden" in der oberen rechten Ecke). Sie sollten etwa Folgendes sehen:

Dies zeigt uns, dass alle Tests für das Beispielprojekt bestanden sind. Sobald Sie dieses Tutorial durchlaufen haben, empfehle ich Ihnen, die spec / PlayerSpec.js Datei und diesen Code durchlesen. Aber jetzt probieren wir es mal mit diesem Test-Schreiben.

  • Erstellen convert.js in dem src Mappe.
  • Erstellen convertSpec.js in dem spez Mappe,
  • Kopiere das SpecRunner.html Datei und benennen Sie es um SpecRunner.original.html.
  • Entfernen Sie die Links zu den Beispielprojektdateien in SpecRunner.html und füge diese Zeilen hinzu:

Jetzt können wir eine Mini-Bibliothek erstellen, die zwischen Maßeinheiten konvertiert. Wir beginnen damit, die Tests für unsere Minibibliothek zu schreiben.


Schritt 3: Schreiben der Tests

Schreiben wir also unsere Tests, oder??

beschreiben ("Bibliothek konvertieren", function () beschreiben ("Entfernungswandler", Funktion () ); beschreiben ("Lautstärkekonverter", Funktion () ););

Wir fangen damit an; wir testen unsere Konvertieren Bibliothek. Sie werden feststellen, dass wir schachteln beschreiben Aussagen hier. Das ist absolut legal. Es ist tatsächlich eine großartige Möglichkeit, separate Funktionsblöcke derselben Codebase zu testen. Anstelle von zwei getrennt beschreiben ruft nach Konvertieren Bibliotheksumwandlungskonvertierungen und Volumenkonvertierungen können wir mit einer Reihe von Tests wie diesen versehen.

Nun zu den eigentlichen Tests. Ich werde das Innere wiederholen beschreiben ruft hier für Ihre Bequemlichkeit an.

beschreiben ("Abstandskonverter", Funktion () it ("Konvertiert Zoll in Zentimeter"), Funktion () Erwarten (Konvertieren (12, "In"). in ("cm")). toEqual (30.48);) ; es ("konvertiert Zentimeter in Yards"), function () erwartet (Convert (2000, "cm"). in ("Yards")). toEqual (21.87);););

Hier sind unsere Tests für Entfernungsumrechnungen. Es ist wichtig, hier etwas zu bemerken: Wir haben keinen Code für unser Programm geschrieben Konvertieren Bibliothek noch, also prüfen wir in diesen Tests mehr als nur, ob es funktioniert: Wir entscheiden tatsächlich, wie es verwendet wird (und daher implementiert wird). So haben wir uns entschieden, unsere Conversions durchzuführen:

Konvertieren(, ).zu();

Ja, ich orientiere mich an der Art und Weise, wie Jasmine seine Tests implementiert hat, aber ich denke, es ist ein schönes Format. In diesen beiden Tests habe ich die Konvertierungen (ok, mit einem Taschenrechner) selbst vorgenommen, um zu sehen, wie die Ergebnisse unserer Aufrufe aussehen sollen. Wir benutzen die toEqual Matcher, um zu sehen, ob unsere Tests bestehen.

Hier sind die Volumentests:

beschreiben ("Volumenkonverter", Funktion () it ("Konvertiert Liter in Gallonen"), Funktion () Erwarten (Konvertieren (3, "Liter"). in ("Gallonen")). toEqual (0,79);) ; es ("konvertiert Gallonen in Tassen"), function () erwartet (Convert (2, "Gallonen"). in ("Tassen")). toEqual (32);););

Und ich werde zwei weitere Tests in unserem Top-Level hinzufügen beschreiben Anruf:

it ("wirft einen Fehler aus, wenn eine unbekannte Einheit von der Unit übergeben wird") function () var testFn = function () Convert (1, "dollar"). in ("yens"); erwarten (testFn) .toThrow ( neuer Fehler ("von Einheit nicht erkannt"));); it ("wirft einen Fehler aus, wenn eine unbekannte Einheit übergeben wird") function () var testFn = function () Convert (1, "cm"). in ("furlongs"); erwarten (testFn) .toThrow ( neuer Fehler ("Nicht erkannte Einheit")););

Diese prüfen auf Fehler, die ausgelöst werden sollen, wenn unbekannte Einheiten in die oder übertragen werden Konvertieren Funktion oder die zu Methode. Sie werden feststellen, dass ich die eigentliche Konvertierung in eine Funktion einpacke und diese an die erwarten von Funktion. Das liegt daran, dass wir die Funktion nicht als aufrufen können erwarten von Parameter; Wir müssen ihm eine Funktion übergeben und die Funktion selbst aufrufen lassen. Da müssen wir dem einen Parameter übergeben zu Funktion können wir so machen.

Die andere Sache ist, dass ich einen neuen Matcher vorstelle: werfen, das nimmt ein Fehlerobjekt. Wir werden uns bald weitere Matcher anschauen.

Nun, wenn Sie sich öffnen SpecRunner.html In einem Browser erhalten Sie Folgendes:

Großartig! Unsere Tests scheitern. Jetzt öffnen wir unsere convert.js feilen und arbeiten:

Funktion Convert (Zahl, fromUnit) Var-Umrechnungen = Abstand: Meter: 1, cm: 0,01, Fuß: 0,3048, Zoll: 0,0254, Yards: 0,9144, Volumen: Liter: 1, Gallonen: 3,785411784, Becher: 0,236588236 , betweenUnit = false, type, unit; for (Typ in Konvertierungen) if (Konvertierungen (Typ)) if ((Einheit = Konvertierungen [Typ] [vonUnit])) ZwischenUnit = Nummer * Einheit * 1000;  return to: function (toUnit) if (betweenUnit) for (Typ in Konvertierungen eingeben) if (conversions.hasOwnProperty (type)) if ((Einheit = Konvertierungen [Typ] [toUnit])) return fix (zwischenUnit / (Einheit * 1000));  werfen neuen Fehler ("Nicht erkannte Einheit");  else werfen neuen Fehler ("von Einheit nicht erkannt");  function fix (num) return parseFloat (num.toFixed (2)); ; 

Wir werden das nicht wirklich besprechen, denn wir lernen hier Jasmin. Aber hier sind die wichtigsten Punkte:

  • Wir machen die Konvertierungen, indem wir die Konvertierung in einem Objekt speichern. Conversion-Nummern werden nach Typ (Entfernung, Volumen, eigene Addition) klassifiziert. Für jedes Messfeld haben wir einen Basiswert (Meter oder Liter), in den alles umgerechnet wird. Also wenn du siehst Yards: 0,9144, Sie wissen, dass so viele Meter in einem Meter sind. Um dann Yards in Zentimeter umzuwandeln, multiplizieren wir Yards durch den ersten Parameter (um die Anzahl der Meter zu erhalten) und teilen Sie dann das Produkt durch cm, die Anzahl der Meter in einem Zentimeter. Auf diese Weise müssen die Konvertierungsraten nicht für jedes Wertepaar gespeichert werden. Dies macht es auch einfach, später neue Werte hinzuzufügen.
  • In unserem Fall erwarten wir, dass die übergebenen Einheiten den Schlüsseln entsprechen, die wir in der Konvertierungstabelle verwenden. Wenn dies eine echte Bibliothek wäre, würden wir mehrere Formate unterstützen wollen, wie z. B. "in", "inch" und "inches" - und daher möchten wir eine Logik hinzufügen, die dem entspricht fromUnit zur rechten Taste.
  • Am Ende von Konvertieren Funktion speichern wir den Zwischenwert in betweenUnit, welches initialisiert wird falsch. Auf diese Weise, wenn wir keine haben fromUnit, betweenUnit wird sein falsch in die gehen zu Methode, und so wird ein Fehler mit geworfen.
  • Wenn wir keine haben toUnit, Ein anderer Fehler wird ausgegeben. Andernfalls teilen wir uns als notwendig auf und geben den konvertierten Wert zurück.

Gehen Sie jetzt zurück zu SpecRunner.html und laden Sie die Seite neu. Sie sollten dies jetzt sehen (nach dem Überprüfen? Show bestanden?):

Da gehts! Unsere Tests sind bestanden. Wenn wir hier ein echtes Projekt entwickeln würden, würden wir Tests für einen bestimmten Teil der Funktionalität schreiben, sie passieren lassen, Tests für eine weitere Prüfung schreiben, sie bestehen lassen, usw. Da dies jedoch ein einfaches Beispiel war, haben wir es gerade gemacht Alles auf einen Streich.

Und nun, da Sie dieses einfache Beispiel für die Verwendung von Jasmine gesehen haben, wollen wir uns ein paar weitere Funktionen ansehen, die Ihnen dies bietet.


Schritt 4: Die Matchers lernen

Bisher haben wir zwei Matcher verwendet: toEqual und werfen. Es gibt natürlich noch viele andere. Hier sind ein paar von denen Sie wahrscheinlich nützlich sein werden; Sie können die gesamte Liste im Wiki sehen.

toBeDefined / toBeUndefined

Wenn Sie nur sicherstellen möchten, dass eine Variable oder Eigenschaft definiert ist, gibt es dafür einen Matcher. Es gibt auch eine, um zu bestätigen, dass eine Variable oder Eigenschaft ist nicht definiert.

it ("ist definiert", function () var name = "Andrew"; erwartet (name) .toBeDefined ();) es ("ist nicht definiert") function () var name; expect (name) .toBeUndefined (););

toBeTruthy / toBeFalsy

Wenn etwas wahr oder falsch sein sollte, tun dies die Matchers.

it ("ist wahr") function () expect (Lib.isAWeekDay ()). toBeTruthy ();); it ("ist falsch") function () expect (Lib.finishedQuiz) .toBeFalsy (););

toBeLessThan / toBeGreaterThan

Für alle, die Sie zählen. Sie wissen, wie diese funktionieren:

es ("ist weniger als 10", function () expect (5) .toBeLessThan (10);); es ("ist größer als 10", function () erwartet (20) .toBeGreaterThan (10););

passen

Haben Sie einen Ausgabetext, der mit einem regulären Ausdruck übereinstimmen sollte? Das passen Matcher ist bereit und willens.

it ("gibt den richtigen Text aus"), function () erwartet (cart.total ()). toMatch (/ \ $ \ d *. \ d \ d /););

enthalten

Dieser ist ziemlich nützlich. Es wird geprüft, ob ein Array oder eine Zeichenfolge ein Element oder eine Teilzeichenfolge enthält.

it ("sollte Orangen enthalten", function () expect (["Äpfel", "Orangen", "Birnen"]). toContain ("Orangen"););

Es gibt auch einige andere Matcher, die Sie im Wiki finden können. Was aber, wenn Sie einen Matcher wollen, der nicht existiert? Eigentlich sollten Sie in der Lage sein, mit etwas Setup-Code und den Matchern, die Jasmine bereitstellt, so ziemlich alles zu tun, aber manchmal ist es schöner, etwas von dieser Logik zu abstrahieren, um einen lesbareren Test zu haben. Zufällig (naja, eigentlich nicht), erlaubt Jasmine uns, eigene Matcher zu erstellen. Aber dazu müssen wir zuerst etwas anderes lernen.

Schritt 5: Vorher und nachher abdecken

Wenn Sie eine Codebasis testen, müssen Sie häufig für jeden Test in einer Reihe einige Zeilen Setup-Code ausführen. Es wäre schmerzhaft und wortreich, das für jeden kopieren zu müssen es rufen Sie an, also hat Jasmine eine praktische kleine Funktion, mit der wir Code festlegen können, der vor oder nach jedem Test ausgeführt werden soll. Mal sehen, wie das funktioniert:

beschreiben ("MyObject", function () var obj = neues MyObject (); beforeEach (function () obj.setState ("clean");); it ("changes state", function () obj.setState.) ("dirty"); erwarten (obj.getState ()). toEqual ("dirty");) it ("fügt Zustände hinzu") function () obj.addState ("packaged"); Expect (obj.getState ( )). toEqual (["sauber", "verpackt"]);));

In diesem Beispiel können Sie sehen, wie der Status von vor jedem Test ausgeführt wird obj ist auf "sauber" eingestellt. Wenn Sie dies nicht tun, bleiben die an einem Objekt in einem vorherigen Test vorgenommenen Änderungen standardmäßig bis zum nächsten Test bestehen. Natürlich könnten wir auch etwas mit dem machen Nach jedem Funktion:

beschreiben ("MyObject", function () var obj = neues MyObject ("clean"); // Setzt den Anfangsstatus nachEach (function () obj.setState ("clean");); , function () obj.setState ("dirty"); erwarten (obj.getState ()). toEqual ("dirty");) it ("fügt Zustände hinzu"), function () obj.addState ("packaged") (; "obj.getState ()). toEqual ([" clean "," packaged "]);));

Hier richten wir das Objekt zunächst ein und lassen es dann korrigieren nach dem jeder Test Wenn du das willst MeinObjekt Damit Sie diesen Code ausprobieren können, können Sie ihn hier in einer GitHub-Liste abrufen.

Schritt 6: Benutzerdefinierte Matcher schreiben

Wie wir schon gesagt haben, wären Matcher zu bestimmten Zeiten wahrscheinlich hilfreich. Schreiben wir also eins. Wir können einen Matcher in einem hinzufügen VorherJedes anrufen oder ein es anrufen (na ja, ich denke du könnte mach es in einer Nach jedem anrufen, aber das würde nicht viel Sinn machen). So fangen Sie an:

beforeEach (function () this.addMatchers (););

Ganz einfach, wie? Wir nennen this.addMatchers, Übergabe eines Objektparameters. Jeder Schlüssel in diesem Objekt wird zum Namen eines Übereinstimmers, und die zugehörige Funktion (der Wert) wird so ausgeführt, wie sie ausgeführt wird. Nehmen wir an, wir möchten einen Matcher erstellen, der überprüft, ob eine Zahl zwischen zwei anderen steht. Folgendes schreiben Sie:

beforeEach (function () this.addMatchers (toBeBetween: Funktion (rangeFloor, rangeCeiling) if (rangeFloor> rangeCeiling) var temp = rangeFloor; rangeFloor = rangeCeiling; rangeCeiling = temp; tatsächlich < rangeCeiling;  ); );

Wir nehmen einfach zwei Parameter, stellen sicher, dass der erste kleiner als der zweite ist, und geben eine boolesche Anweisung zurück, die den Wert true ergibt, wenn unsere Bedingungen erfüllt sind. Das Wichtigste dabei ist, wie wir den Wert erhalten, der an das Unternehmen weitergegeben wurde erwarten von Funktion: this.actual.

es ("ist zwischen 5 und 30") function () erwartet (10) .toBeBetween (5, 30);); es ("ist zwischen 30 und 500") function () erwartet (100) .toBeBetween (500, 30););

Das ist was der SpecHelper.js Datei macht; es hat ein vor jedem Aufruf, der den Matcher hinzufügt tobePlaying (). Hör zu!


Fazit: Spaß haben!

Mit Jasmine können Sie noch viel mehr tun: funktionsbezogene Matcher, Spione, asynchrone Spezifikationen und mehr. Ich empfehle Ihnen, das Wiki zu erkunden, wenn Sie interessiert sind. Es gibt auch einige begleitende Bibliotheken, die das Testen im DOM einfacher machen: Jasmine-jQuery und Jasmine-Fixture (abhängig von Jasmine-jQuery).

Wenn Sie also Ihr JavaScript noch nicht getestet haben, ist jetzt ein guter Zeitpunkt, um zu beginnen. Wie wir gesehen haben, macht die schnelle und einfache Syntax von Jasmine das Testen ziemlich einfach. Es gibt einfach keinen Grund für Sie, es nicht zu tun, jetzt ist es da?

Wenn Sie Ihre JavaScript-Entwicklung weiterführen möchten, schauen Sie sich die Palette der JavaScript-Artikel auf Envato Market an. Es gibt Tausende von Skripten, Apps und Code-Snippets, die Ihnen helfen.