JavaScript-Tests von Grund auf

Dies ist wahrscheinlich nicht das erste Tutorial zum Testen, das Sie jemals gesehen haben. Aber vielleicht hatten Sie Ihre Zweifel beim Testen und haben sich nie die Zeit genommen, sie zu lesen. Immerhin kann es ohne Grund als zusätzliche Arbeit erscheinen.

Dieses Tutorial soll Ihre Ansichten ändern. Wir fangen ganz am Anfang an: Was testen wir und warum sollten Sie es tun? Dann sprechen wir kurz über das Schreiben von testfähigem Code, bevor Sie tatsächlich Tests durchführen! Lasst uns anfangen!


Ich bevorzuge einen Screencast?

Teil 1


Teil 2


Teil 3



Test definieren

Recht einfach, testen Dies ist die Idee einer Reihe von Anforderungen, die ein bestimmter Code erfüllen muss, um robust genug zu sein, um in der realen Welt verwendet zu werden. Häufig schreiben wir etwas JavaScript, öffnen es dann im Browser und klicken ein wenig herum, um sicherzustellen, dass alles wie erwartet funktioniert. Obwohl dies manchmal notwendig ist, ist es nicht die Art von Tests, über die wir hier sprechen. In der Tat hoffe ich, dass dieses Tutorial Sie davon überzeugen wird, dass schnelle und schmutzige Selbsttests ein strengeres Testverfahren ergänzen sollten: Selbsttests sind in Ordnung, aber eine gründliche Liste von Anforderungen ist von größter Bedeutung.


Gründe für das Testen

Wie Sie sich vorstellen können, ist das Problem beim JavaScript-Test zum Aktualisieren und Klicken ein doppeltes Problem:

  1. Vielleicht erinnern wir uns nicht daran, etwas zu überprüfen; Selbst wenn wir dies tun, überprüfen wir die Code-Änderungen möglicherweise nicht noch einmal.
  2. Möglicherweise gibt es einige Teile des Codes, die auf diese Weise nicht wirklich testbar sind.

Durch schreiben Tests Wenn Sie alles überprüfen, was Ihr Code tun soll, können Sie überprüfen, ob Ihr Code in bestem Zustand ist, bevor Sie ihn auf einer Website verwenden. Wenn tatsächlich etwas in einem Browser ausgeführt wird, gibt es wahrscheinlich mehrere Fehlerquellen. Durch das Schreiben von Tests können Sie sich auf jedes zu prüfende Teil einzeln konzentrieren. Wenn jedes Teil seine Arbeit richtig macht, sollten die Dinge ohne Probleme zusammenarbeiten (das Testen einzelner Teile wie diesem wird als Einheitstest bezeichnet)..


Testbaren Code schreiben

Wenn Sie ein Programmiersprache sind, haben Sie möglicherweise in anderen Sprachen getestet. Aber ich habe Tests in JavaScript als ein anderes Tier zum Töten gefunden. Schließlich bauen Sie nicht zu viele Benutzeroberflächen in PHP oder Ruby auf. Oft machen wir DOM-Arbeit in JavaScript, und wie genau testen Sie das??

Nun, die DOM-Arbeit ist nicht das, wofür Sie Tests schreiben wollen; Es ist die Logik. Der Schlüssel ist hier natürlich, Ihre Logik und Ihren UI-Code voneinander zu trennen. Das ist nicht immer einfach; Ich habe meinen gerechten Anteil an der jQuery-basierten Benutzeroberfläche geschrieben und kann ziemlich schnell unordentlich werden. Dies macht es nicht nur schwierig zu testen, sondern auch, dass Logik und UI-Code miteinander verflochten sind, wenn sich das gewünschte Verhalten ändert. Ich habe festgestellt, dass die Verwendung von Methoden wie Vorlagen (auch Vorlagen) und pub / sub (auch pub / sub) das Schreiben von besserem, überprüfbarem Code vereinfacht.

Noch eine Sache, bevor wir mit dem Programmieren beginnen: Wie schreiben wir unsere Tests? Es gibt zahlreiche Testbibliotheken, die Sie verwenden können (und viele gute Tutorials, die Sie in ihrer Verwendung unterrichten; sehen Sie die Links als Ende). Wir werden jedoch eine kleine Testbibliothek von Grund auf erstellen. Es ist nicht so schick wie in manchen Bibliotheken, aber Sie werden genau sehen, was los ist.

Lasst uns in diesem Sinne an die Arbeit gehen!


Test-Mini-Framework erstellen

Wir werden eine Mikro-Fotogalerie erstellen: eine einfache Liste von Miniaturansichten, auf denen ein Bild in voller Größe angezeigt wird. Aber zuerst bauen wir die Testfunktion aus.

Wenn Sie mehr über das Testen und Testen von Bibliotheken erfahren, finden Sie zahlreiche Testmethoden zum Testen aller Arten von Besonderheiten. Es kann jedoch alles darauf reduziert werden, ob zwei Dinge gleich sind oder nicht: zum Beispiel,

  • Ist der von dieser Funktion zurückgegebene Wert gleich dem, von dem wir erwartet hatten, dass er zurückkommt??
  • Ist diese Variable von dem Typ, von dem wir sie erwartet haben??
  • Hat dieses Array die erwartete Anzahl von Elementen??

Hier ist unsere Methode, um die Gleichheit zu testen:

var TEST = areEqual: Funktion (a, b, msg) var Ergebnis = (a === b); console.log ((Ergebnis? "PASS:": "FAIL:") + msg); Ergebnis zurückgeben; ;

Es ist ziemlich einfach: Die Methode benötigt drei Parameter. Die ersten beiden werden verglichen, und wenn sie gleich sind, werden die Tests bestanden. Der dritte Parameter ist eine Nachricht, die den Test beschreibt. In dieser einfachen Testbibliothek geben wir unsere Tests nur auf der Konsole aus. Sie können jedoch HTML-Ausgabe mit entsprechendem CSS-Stil erstellen, wenn Sie möchten.

Hier ist die areNotEqual Methode (innerhalb der gleichen PRÜFUNG Objekt):

areNotEqual: Funktion (a, b, msg) var Ergebnis = (a! == b); console.log ((Ergebnis? "PASS:": "FAIL:") + msg); Ergebnis zurückgeben; 

Sie werden die letzten zwei Zeilen von sehen sind gleich und areNotEqual das Gleiche. So können wir diese so herausziehen:

var TEST = areEqual: Funktion (a, b, msg) return this._output (a === b, msg), areNotEqual: function (a, b, msg) return this._output (a! == b, msg); , _output: function (Ergebnis, msg) Konsole [Ergebnis? "log": "warn"] ((Ergebnis? "PASS:": "FAIL:") + msg); Ergebnis zurückgeben; ;

Großartig! Das Besondere daran ist, dass wir andere Methoden für Zucker hinzufügen können, indem wir die Methoden verwenden, die wir bereits geschrieben haben:

TEST.isTypeOf = function (obj, type, msg) return this.areEqual (typeof obj, type, msg); ; TEST.isAnInstanceOf = Funktion (obj, type, msg) return this._output (obj-Instanz des Typs, msg);  TEST.isGreaterThan = Funktion (val, min, msg) return this._output (val> min, msg); 

Sie können dies selbst ausprobieren. Nachdem Sie dieses Tutorial durchgearbeitet haben, haben Sie eine gute Vorstellung davon, wie es verwendet wird.


Vorbereitung auf unsere Galerie

Also, erstellen wir mit unserem Mini eine extrem einfache Fotogalerie PRÜFUNG Framework, um einige Tests zu erstellen. Ich möchte an dieser Stelle erwähnen, dass die testgetriebene Entwicklung zwar eine großartige Übung ist, wir werden sie jedoch nicht in diesem Lernprogramm verwenden, vor allem weil es nicht etwas ist, das Sie in einem einzigen Lernprogramm lernen können. Es braucht wirklich viel Übung, um wirklich zu funktionieren Grok. Wenn Sie anfangen, ist es einfacher, etwas Code zu schreiben und dann zu testen.

So lass uns anfangen. Natürlich benötigen wir HTML für unsere Galerie. Wir werden das ziemlich einfach halten:

     Testen in JavaScript     

Es gibt zwei wichtige Dinge, die es zu beachten gilt: erstens haben wir ein

das hält die sehr einfache Markierung für unsere Bildergalerie. Nein, es ist wahrscheinlich nicht sehr robust, aber es gibt etwas, mit dem wir arbeiten können. Beachten Sie dann, dass wir drei anschließen >s: one ist unsere kleine Testbibliothek (siehe oben). Eine ist die Galerie, die wir erstellen werden. Der letzte Test enthält die Tests für unsere Galerie. Beachten Sie auch die Pfade zu den Bildern: An die Miniaturbild-Dateinamen werden "-thumb" angehängt. So finden wir die größere Version des Bildes.

Ich weiß, dass Sie nach Codierung jucken wollen, also werfen Sie dies in eine gallery.css Datei:

.Galerie Hintergrund: #ececec; Überlauf versteckt; Breite: 620px;  .gallery> img margin: 20px 20px 0; Polsterung: 0;  .gallery ul list-style-type: none; Marge: 20px; Polsterung: 0; Überlauf versteckt;  .gallery li float: left; Marge: 0 10px;  .gallery li: first-of-type margin-left: 0px;  .gallery li: last-of-type margin-right: 0px; 

Jetzt können Sie dies in Ihrem Browser laden und Folgendes sehen:

OKAY BEREITS! Lassen Sie uns etwas JavaScript schreiben, sollen wir?


Umreißen unserer Galerie

Mach das auf gallery.js Datei. So fangen wir an:

var Gallery = (function () var Gallery = , galleryPrototype = ; Gallery.create = Funktion (id) var gal = Object.create (galleryPrototype); return gal;; return Gallery; ()) ;

Wir werden noch mehr hinzufügen, aber das ist ein guter Anfang. Wir haben eine selbstaufrufende anonyme Funktion (oder einen sofort aufgerufenen Funktionsausdruck) verwendet, um alles zusammenzuhalten. Unser "Inneres" Galerie Variable wird zurückgegeben und ist der Wert unseres Publikums Galerie Variable. Wie Sie sehen können, rufen Sie an Gallery.create erstellt ein neues Galerieobjekt mit Object.create. Wenn Sie nicht vertraut sind Object.create, Es erstellt einfach ein neues Objekt mit dem Objekt, das Sie als Prototyp des neuen Objekts übergeben (es ist auch ziemlich Browser-konform). Wir werden diesen Prototyp ausfüllen und unseren ergänzen Gallery.create Methode auch. Aber jetzt schreiben wir unseren ersten Test:

var gal = Gallery.create ("gal-1"); TEST.areEqual (typeof gal, "object", "Gallery sollte ein Objekt sein");

Wir beginnen mit der Erstellung einer "Instanz" von Galerie; Dann führen wir einen Test durch, um zu sehen, ob der zurückgegebene Wert ein Objekt ist.

Setze diese beiden Zeilen in unsere gallery-test.js; jetzt öffne unsere index.html Seite in einem Browser und öffnen Sie eine JavaScript-Konsole. Sie sollten so etwas sehen:


Großartig! Unser erster Test ist bestanden!


Konstruktor schreiben

Als nächstes füllen wir unsere aus Gallery.create Methode. Wie Sie sehen, machen wir uns keine Gedanken darüber, ob dieser Beispielcode äußerst robust ist. Daher verwenden wir einige Dinge, die nicht in jedem Browser, der jemals erstellt wurde, kompatibel sind. Nämlich, document.querySelector / document.querySelectorAll; Außerdem verwenden wir nur eine moderne Ereignisbehandlung für Browser. Fühlen Sie sich frei, Ihre Lieblingsbibliothek zu ersetzen, wenn Sie möchten.

Fangen wir also mit einigen Erklärungen an:

var gal = Object.create (galleryPrototype), ul, i = 0, len; gal.el = document.getElementById (id); ul = gal.el.querySelector ("ul"); gal.imgs = gal.el.querySelectorAll ("ul li img"); gal.displayImage = gal.el.querySelector ("img: erstes Kind"); gal.idx = 0; gal.going = false; gal.ids = [];

Vier Variablen: insbesondere unser Galerieobjekt und die Galerie

    Knoten (wir werden verwenden) ich und len in einer Minute). Dann sechs Eigenschaften auf unserer gal Objekt:

    • gal.el ist der Wurzelknoten auf dem Markyp unserer Galerie.
    • gal.imgs ist eine Liste der
    • s halten unsere Thumbnails.
    • gal.displayImage ist das große Bild in unserer Galerie.
    • gal.idx ist der Index, das aktuell angezeigte Bild.
    • gal.going ist ein Boolean: es ist wahr Wenn die Galerie die Bilder durchläuft.
    • gal.ids wird eine Liste der IDs für die Bilder in unserer Galerie sein. Wenn das Miniaturbild beispielsweise "dog-thumb.jpg" heißt, dann ist "dog" die ID und "dog.jpg" das Bild der Anzeigegröße.

    Beachten Sie, dass DOM-Elemente haben querySelector und querySelectorAll Methoden auch. Wir können benutzen gal.el.querySelector um sicherzustellen, dass wir nur Elemente innerhalb der Auszeichnung dieser Galerie auswählen.

    Füllen Sie jetzt aus gal.ids mit den Bild-IDs:

    len = gal.imgs.length; für (; i < len; i++ )  gal.ids[i] = gal.imgs[i].getAttribute("src").split("-thumb")[0].split("/")[1]; 

    Ziemlich unkompliziert, richtig?

    Lassen Sie uns zum Schluss noch einen Event-Handler auf der

      .

      ul.addEventListener ("click", Funktion (e) var i = [] .indexOf.call (gal.imgs, e.target); if (i> -1) gal.set (i);, falsch);

      Wir prüfen zunächst, ob das unterste Element den Klick erhalten hat (e.target; Wir machen uns keine Sorgen um oldIE-Unterstützung (hier in unserer Bildliste); schon seit NodeListIch habe keine Index von Methode verwenden wir die Array-Version (wenn Sie nicht mit vertraut sind Anruf und sich bewerben In JavaScript finden Sie unseren kurzen Tipp zu diesem Thema.). Wenn der Wert größer als -1 ist, übergeben wir ihn an gal.set. Wir haben diese Methode noch nicht geschrieben, aber wir werden darauf eingehen.

      Kommen wir zu unserem zurück gallery-test.js Datei und schreiben Sie einige Tests, um sicherzustellen, dass unsere Galerie Instanz hat die richtigen Eigenschaften:

      TEST.areEqual (gal.el.id, "gal-1", "Gallery.el sollte derjenige sein, den wir angegeben haben"); TEST.areEqual (gal.idx, 0, "Galerieindex sollte bei Null beginnen");

      Unser erster Test zeigt, dass unser Galeriekonstruktor das richtige Element gefunden hat. Der zweite Test überprüft, ob der Index bei 0 beginnt. Sie könnten wahrscheinlich eine Reihe von Tests schreiben, um zu überprüfen, ob wir die richtigen Eigenschaften haben. Wir werden Tests für die Methoden schreiben, die diese Eigenschaften verwenden. Das ist also nicht der Fall nötig sein.


      Den Prototyp bauen

      Nun zurück zu dem GaleriePrototyp Objekt, das derzeit leer ist. Hier werden wir alle Methoden unseres unterbringen Galerie "Instanzen". Beginnen wir mit dem einstellen Methode: Dies ist die wichtigste Methode, da sie das angezeigte Bild tatsächlich ändert. Es nimmt entweder den Index des Bildes oder die ID-Zeichenfolge des Bildes.

      // innerhalb von 'galleryProtytype' set: function (i) if (typeof i === 'string') i = this.ids.indexOf (i);  this.displayImage.setAttribute ("src", "images /" + this.ids [i] + ".jpg"); return (this.idx = i); 

      Wenn die Methode die ID-Zeichenfolge erhält, findet sie die korrekte Indexnummer für diese ID. Dann setzen wir die Bild anzeigen's src auf den richtigen Bildpfad und geben Sie den neuen aktuellen Index zurück, während Sie ihn als aktuellen Index festlegen.

      Lassen Sie uns nun diese Methode testen (wieder rein) gallery-test.js):

      TEST.areEqual (gal.set (4), 4, "Gallery.set (mit Nummer) sollte dieselbe übergebene Nummer zurückgeben"); TEST.areEqual (gal.displayImage.getAttribute ("src"), "images_24 / javascript-testing-from-scratch.jpg", "Gallery.set (mit Nummer) sollte das angezeigte Bild ändern"); TEST.areEqual (gal.set ("post"), 3, "Gallery.set (mit String) sollte sich zum entsprechenden Bild bewegen"); TEST.areEqual (gal.displayImage.getAttribute ("src"), "images / post.jpg", "Gallery.set (mit String) sollte die angezeigten Bilder ändern");

      Wir testen unsere Testmethode sowohl mit einem Zahlen- als auch einem Zeichenfolgenparameter für einstellen. In diesem Fall können wir das überprüfen src für das Bild und stellen Sie sicher, dass die Benutzeroberfläche entsprechend angepasst wird; Es ist nicht immer möglich oder notwendig, sicherzustellen, dass das, was der Benutzer sieht, angemessen reagiert (ohne etwas davon zu verwenden). Hier ist der Click-around-Test nützlich. Wir können es jedoch hier tun, also werden wir es tun.

      Diese Tests sollten bestehen. Sie sollten auch in der Lage sein, auf die Miniaturbilder zu klicken und die größeren Versionen anzeigen zu lassen. Gut aussehend!

      Lassen Sie uns nun zu einigen Methoden übergehen, die sich zwischen den Bildern bewegen. Dies kann nützlich sein, wenn Sie die Schaltflächen "Nächste" und "Vorherige" verwenden möchten, um zwischen den Bildern zu wechseln (diese Schaltflächen werden nicht angezeigt, aber wir setzen die unterstützenden Methoden ein.).

      In den meisten Fällen ist das Wechseln zu den nächsten und vorherigen Bildern keine schwierige Sache. Die kniffligen Teile gehen beim letzten Bild zum nächsten Bild oder beim ersten zum vorherigen Bild.

      // inside 'galleryPrototype' next: function () if (this.idx === this.imgs.length - 1) return this.set (0);  return this.set (this.idx + 1) zurückgeben; , prev: function () if (this.idx === 0) return this.set (this.imgs.length - 1);  return this.set (this.idx - 1) zurückgeben; , curr: function () return this.idx; ,

      Okay, das ist eigentlich nicht so schwierig. Beide Methoden sind "Zuckermethoden" einstellen. Wenn wir beim letzten Bild sind (this.idx === this.imgs.length -1), wir Set (0). Wenn wir am ersten sind (this.idx === 0), wir set (this.imgs.length -1). Andernfalls fügen Sie einfach einen Wert vom aktuellen Index hinzu oder ziehen ihn davon ab. Vergiss nicht, dass wir genau das zurückgeben, was vom zurückgegeben wird einstellen Anruf.

      Wir haben auch die curr Methode auch. Es ist überhaupt nicht kompliziert: es gibt nur den aktuellen Index zurück. Wir werden es später testen.

      Also lassen Sie uns diese Methoden testen.

      TEST.areEqual (gal.next (), 4, "Galerie sollte auf .next () vorrücken"); TEST.areEqual (gal.prev (), 3, "Galerie sollte auf .prev () zurückkehren");

      Diese kommen nach unseren vorherigen Tests, also sind 4 und 3 die Werte, die wir erwarten würden. Und sie gehen vorbei!

      Es ist nur noch ein Teil übrig: Das ist das automatische Foto-Radfahren. Wir möchten anrufen können gal.start () durch die Bilder spielen. Natürlich werden sie ein gal.stop () Methode.

      // in 'galleryPrototype' start: function (Zeit) var thiz = this; Zeit = Zeit || 3000; this.interval = setInterval (function () thiz.next ();, Zeit); this.going = true; wahr zurückgeben; , stop: function () clearInterval (this.interval); this.going = false; wahr zurückgeben; ,

      Unsere Start Die Methode benötigt Parameter: die Anzahl der Millisekunden, die ein Bild angezeigt wird; Wenn kein Parameter angegeben wird, wird standardmäßig 3000 (3 Sekunden) verwendet. Dann benutzen wir einfach setInterval auf eine Funktion, die anruft Nächster zur richtigen Zeit. Natürlich können wir das Einstellen nicht vergessen this.going zu wahr. Schließlich kehren wir zurück wahr.

      halt ist nicht zu schwer. Da haben wir das Intervall als gespeichert this.interval, wir können benutzen clearInterval um es zu beenden Dann setzen wir uns this.going falsch und kehren zurück.

      Wir haben zwei praktische Funktionen, um zu informieren, ob die Galerie eine Schleife durchläuft:

      isGoing: function () return this.going; , isStopped: function () return! this.going; 

      Lassen Sie uns nun diese Funktionalität testen.

      gal.set (0); TEST.areEqual (gal.start (), true, "Galerie sollte Schleife sein"); TEST.areEqual (gal.curr (), 0, "Der aktuelle Bildindex sollte 0 sein")); setTimeout (function () TEST.areEqual (gal.curr (), 1, "Der aktuelle Bildindex sollte 1 sein")) TEST.areEqual (gal.isGoing (), true, "Galerie sollte laufen"); TEST. areEqual (gal.stop (), true, "Galerie sollte angehalten werden"); setTimeout (function () TEST.areEqual (gal.curr (), 1, "Aktuelles Bild sollte noch 1 sein"); TEST.areEqual ( gal.isStopped (), true, "Galerie sollte noch angehalten werden");, 3050);, 3050);

      Es ist etwas komplizierter als bei unseren vorherigen Tests: Wir beginnen mit der Verwendung gal.set (0) Um sicher zu gehen, fangen wir am Anfang an. Dann rufen wir an gal.start () um das Looping zu starten. Als nächstes testen wir das gal.curr () gibt 0 zurück, dh wir sehen immer noch das erste Bild. Jetzt benutzen wir eine setTimeout 3050ms warten (nur etwas mehr als 3 Sekunden), bevor Sie unsere Tests fortsetzen. Darin drin setTimeout, wir machen noch einen gal.curr (); Der Index sollte jetzt 1 sein. Dann testen wir das gal.isGoing () ist wahr. Als Nächstes stoppen wir die Galerie gal.stop (). Jetzt benutzen wir eine andere setTimeout weitere 3 Sekunden warten; Wenn die Galerie wirklich angehalten hat, wird das Bild nicht wiederholt gal.curr () sollte noch 1 sein; Das testen wir innerhalb des Timeouts. Zum Schluss sorgen wir dafür, dass ist gestoppt Methode funktioniert.

      Wenn diese Tests bestanden sind, herzlichen Glückwunsch! Wir haben unsere abgeschlossen Galerie und seine Tests.


      Fazit

      Wenn Sie das Testen noch nicht ausprobiert haben, hoffe ich, dass Sie gesehen haben, wie einfach das Testen in JavaScript sein kann. Wie ich bereits zu Beginn dieses Tutorials erwähnt habe, müssen Sie bei guten Tests wahrscheinlich etwas anderes JavaScript schreiben, als Sie vielleicht gewohnt sind. Ich habe jedoch festgestellt, dass leicht überprüfbares JavaScript auch leicht zu pflegendes JavaScript ist.

      Ich lasse Ihnen einige Links, die Sie als nützlich erachten könnten, wenn Sie gute JavaScript und gute Tests schreiben.

      • QUnit - eine Unit-Test-Suite (Tutorial zu QUnit)
      • Jasmin - BDD-Framework (Tutorial über Jasmin)
      • JavaScript-Test mit Assert
      • Test Driven JavaScript (Buch) (Beispielkapitel)