Jasmine ist die beliebteste JS-Bibliothek für Unit-Tests von Web-Apps. In diesem Tutorial für Anfänger präsentieren wir Ihnen eine schnelle und vollständige Anleitung zum Testen mit Jasmine.
Sie werden in Jasmine eingeführt, ein beliebtes verhaltensgesteuertes Testframework für JavaScript. Wir sehen auch ein einfaches praktisches Beispiel zum Schreiben von Komponententests mit Jasmine, mit dem Sie problemlos nach Fehlern in Ihrem Code suchen können.
Kurz gesagt, wir werden sehen, wie man Testsuiten, Spezifikationen und Erwartungen schreibt und wie man integrierte Jasmine-Matcher anwendet oder eigene benutzerdefinierte Matcher erstellt
Wir werden auch sehen, wie Sie Suites gruppieren können, um Ihre Tests für komplexere Codebasen zu organisieren.
Einführung in Jasmine
Jasmine ist ein sehr beliebtes JavaScript-Framework für verhaltensgesteuerte Entwicklung (In BDD schreiben Sie Tests, bevor Sie tatsächlichen Code schreiben) zum Testen von JavaScript-Anwendungen. Es bietet Dienstprogramme, mit denen automatisierte Tests für synchronen und asynchronen Code ausgeführt werden können.
Jasmine hat viele Funktionen wie:
- Es ist schnell und hat einen geringen Overhead und keine externen Abhängigkeiten.
- Es ist eine komplett mitgelieferte Bibliothek und bietet alles, was Sie zum Testen Ihres Codes benötigen.
- Es ist sowohl für Node als auch für den Browser verfügbar.
- Es kann mit anderen Sprachen wie Python und Ruby verwendet werden.
- Das DOM ist nicht erforderlich.
- Es bietet eine saubere und leicht verständliche Syntax und auch eine reichhaltige und unkomplizierte API.
- Wir können natürliche Sprache verwenden, um die Tests und die erwarteten Ergebnisse zu beschreiben.
Jasmine ist ein Open-Source-Tool, das unter der freizügigen MIT-Lizenz verfügbar ist. Zum Zeitpunkt des Schreibens ist die neueste Hauptversion Jasmine 3.0, die neue Funktionen und einige wichtige Änderungen bietet. Die Version 2.99 von Jasmine enthält verschiedene Verfallswarnungen für Suiten, die sich in Version 3.0 unterschiedlich verhalten, was es Entwicklern erleichtert, auf die neue Version zu migrieren.
Sie können mehr über die neuen Funktionen und Änderungen in diesem Dokument lesen.
Verwendung von Jasmine
Sie können Jasmine auf viele verschiedene Arten verwenden:
- auf die alte Art und Weise, indem Sie sowohl den Jasmine-Kern als auch Ihre Testdateien mit einem
<scri
pt> -Tag - als CLI-Tool mit Node .js,
- als Bibliothek im Knoten.js,
- als Teil eines Build-Systems wie Gulp.js oder Grunzen.js über grunt-contrib-jasmine und gulp-jasmine-browser
Sie können Jasmine auch zum Testen Ihres Python-Codes mit jasmine-py verwenden, der mit dem Befehl pip install jasmine
von PyPI installiert werden kann. Dieses Paket enthält sowohl einen Webserver, der eine Jasmine-Suite für Ihr Projekt bereitstellt und ausführt, als auch ein CLI-Skript zum Ausführen von Tests und kontinuierlichen Integrationen.
Jasmine ist auch für Ruby-Projekte über jasmine-gem verfügbar, das durch Hinzufügen von gem 'jasmine'
zu Ihrer Gemfile und Ausführen von bundle install
installiert werden kann. Es enthält einen Server zum Bereitstellen und Ausführen von Tests, ein CLI-Skript und Generatoren für Ruby on Rails-Projekte.
Konzentrieren wir uns nun auf die Verwendung von Jasmine mit JavaScript:
Verwenden von eigenständigem Jasmine
Laden Sie zunächst die neueste Version von Jasmine von der Seite Releases herunter.
Extrahieren Sie dann einfach die ZIP-Datei, vorzugsweise in einem Ordner des Projekts, das Sie testen möchten.
Der Ordner enthält eine Reihe von Standarddateien und -ordnern:
/src
: enthält die Quelldateien, die Sie testen möchten. Dies kann entweder gelöscht werden, wenn der Ordner Ihres Projekts bereits eingerichtet ist, oder kann auch zum Hosten Ihres Quellcodes verwendet werden.
/lib
: enthält die Jasmine-Kerndateien.
/spec
: enthält die Tests, die Sie schreiben werden.
SpecRunner.html
: Diese Datei wird als Testlauf verwendet. Sie führen Ihre Spezifikationen aus, indem Sie einfach diese Datei starten.
Dies ist der Inhalt einer Standarddatei SpecRunner.html
:
<!DOCTYPE html><html><head> <meta charset="utf-8"> <title>Jasmine Spec Runner v3.2.1</title> <link rel="shortcut icon" type="image/png" href="lib/jasmine-3.2.1/jasmine_favicon.png"> <link rel="stylesheet" href="lib/jasmine-3.2.1/jasmine.css"> <script src="lib/jasmine-3.2.1/jasmine.js"></script> <script src="lib/jasmine-3.2.1/jasmine-html.js"></script> <script src="lib/jasmine-3.2.1/boot.js"></script> <!-- include source files here... --> <script src="src/Player.js"></script> <script src="src/Song.js"></script> <!-- include spec files here... --> <script src="spec/SpecHelper.js"></script> <script src="spec/PlayerSpec.js"></script></head><body></body></html>
Denken Sie daran, dass Sie die enthaltenen Dateien aus den Ordnern /src
und /spec
so ändern müssen, dass sie Ihre tatsächlichen Quell- und Testdateien enthalten.
Jasmine als Bibliothek verwenden
Sie können Jasmine auch als Bibliothek in Ihrem Projekt verwenden. Zum Beispiel importiert und führt der folgende Code Jasmine aus:
var Jasmine = require('jasmine');var jasmine = new Jasmine();jasmine.loadConfigFile('spec/support/jasmine.json');jasmine.execute();
Zuerst benötigen/importieren wir Jasmine und verwenden die loadConfigFile()
Methode, um die Konfigurationsdatei zu laden, die unter spec/support/jasmine.json
Pfad verfügbar ist.
Verwenden von Jasmine über die CLI
Sie können Jasmine auch über die CLI verwenden, um Jasmine-Tests einfach auszuführen und die Ergebnisse standardmäßig im Terminal auszugeben.Führen Sie also zuerst den folgenden Befehl aus, um Jasmine global zu installieren:
npm install -g jasmine
Möglicherweise müssen Sie sudo ausführen, um npm-Pakete global zu installieren, abhängig von Ihrer npm-Konfiguration.
Erstellen Sie nun einen Ordner für Ihr Projekt und navigieren Sie darin:
$ mkdir jasmine-project $ cd jasmine-project
Führen Sie als nächstes den folgenden Befehl aus, um Ihr Projekt für Jasmine zu initialisieren:
Dieser Befehl erstellt einfach einen Spezifikationsordner und eine JSON-Konfigurationsdatei. Dies ist die Ausgabe des dir
Befehls:
.└── spec └── support └── jasmine.json2 directories, 1 file
Dies ist der Inhalt einer Standard jasmine.json
Datei:
{ "spec_dir": "spec", "spec_files": pec.js" ], "helpers": , "stopSpecOnExpectationFailure": false, "random": true}
-
spec_dir
: Gibt an, wo Jasmine nach Testdateien sucht. -
spec_files
: Gibt die Muster von Testdateien an, standardmäßig alle JS-Dateien, die mit Spec- oder Spec-Zeichenfolgen enden. -
helpers
: Gibt an, wo Jasmine nach Hilfedateien sucht. Hilfedateien werden vor Spezifikationen ausgeführt und können zum Definieren benutzerdefinierter Matcher verwendet werden. -
stopSpecOnExpectationFailure
: Wenn auf true gesetzt, wird eine Spezifikation beim ersten Ausfall einer Erwartung sofort gestoppt (kann als CLI-Option über--stop-on-failure
verwendet werden). -
random
: Wenn Jasmine auf true gesetzt ist, werden die Testfälle pseudo-zufällig ausgeführt (kann als CLI-Option über--random
verwendet werden).
Die Arrays spec_files
und helpers
können auch Glob-Muster enthalten (dank des node-glob-Pakets), um Dateipfade anzugeben, die Sie normalerweise verwenden, um eine Reihe von Dateien anzugeben, wenn Sie in Bash arbeiten (z. B. ls *.js
).
Wenn Sie den Standardspeicherort für die jasmine.json
-Konfigurationsdatei nicht verwenden, müssen Sie einfach den benutzerdefinierten Speicherort über die jasmine --config
-Option angeben.
Weitere CLI-Optionen finden Sie in den offiziellen Dokumenten.
Jasmine verstehen
In diesem Abschnitt erfahren Sie mehr über die grundlegenden Elemente von Jasmine-Tests wie Suiten, Spezifikationen, Erwartungen, Matcher und Spione usw.
Führen Sie im Ordner Ihres Projekts den folgenden Befehl aus, um ein neues Knotenmodul zu initialisieren:
Dadurch wird eine package.json
Datei mit Standardinformationen erstellt:
{ "name": "jasmine-project", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": , "author": "", "license": "ISC"}
Erstellen Sie als nächstes eine index.js
Datei und fügen Sie den folgenden Code hinzu:
function fibonacci(n){ if (n === 1) { return ; } else { var s = fibonacci(n - 1); s.push(s + s); return s; }}function isPrime(num){ for (let i = 2; i < num; i++) if (num % i === 0) return false; return num !== 1 && num !== 0;}function isEven(n) { return n % 2 == 0;}function isOdd(n) { return Math.abs(n % 2) == 1;}function toLowerCase(str){ return str.toLowerCase();}function toUpperCase(str){ return str.toUpperCase();}function contains(str, substring, fromIndex){ return str.indexOf(substring, fromIndex) !== -1;}function repeat(str, n){ return (new Array(n + 1)).join(str);}module.exports = { fibonacci: fibonacci, isPrime: isPrime, isEven: isEven, isOdd: isOdd, toLowerCase: toLowerCase, toUpperCase: toUpperCase, contains: contains, repeat: repeat};
Suites
Eine Suite gruppiert eine Reihe von Spezifikationen oder Testfällen. Es wird verwendet, um ein bestimmtes Verhalten des JavaScript-Codes zu testen, das normalerweise von einem Objekt / einer Klasse oder einer Funktion gekapselt wird. Es wird mit der globalen Jasmine-Funktion describe()
erstellt, die zwei Parameter verwendet, den Titel der Testsuite und eine Funktion, die den tatsächlichen Code der Testsuite implementiert.
Beginnen wir mit der Erstellung unserer ersten Testsuite. Erstellen Sie im Ordner spec
eine MyJSUtilitiesSpec.js
-Datei und fügen Sie Folgendes hinzu:
describe("MyJSUtilities", function() { /* ... */ });
MyJSUtilities ist der Name dieser Testsuite der obersten Ebene.
Gruppieren und Verschachteln von Suiten
Um unsere Tests besser zu organisieren und genau zu beschreiben, können wir Suiten in der Suite der obersten Ebene verschachteln. Fügen wir der MyJSUtilities-Suite beispielsweise zwei Suiten hinzu:
describe("String Utils", function() { /*...*/});describe("Math Utils", function() { /*...*/});
Fügen wir in der Math Utils-Suite auch zwei verschachtelte Suiten hinzu:
describe("Basic Math Utils", function() { /* ... */ }); describe("Advanced Math Utils", function() { /* ... */ });
Wir gruppieren verwandte Tests in Tests für String-Utils, Basic Math Utils und Advanced Math Utils und verschachteln sie in der Testsuite der obersten Ebene MyJSUtilities. Dadurch werden Ihre Spezifikationen als Bäume erstellt, die einer Struktur von Ordnern ähneln.
Die Verschachtelungsstruktur wird im Bericht angezeigt, was es Ihnen leicht macht, fehlgeschlagene Tests zu finden.
So schließen Sie Suiten aus
Sie können eine Suite vorübergehend deaktivieren, indem Sie die Funktion xdescribe()
verwenden. Es hat die gleiche Signatur (Parameter) wie eine describe()
Funktion, was bedeutet, dass Sie Ihre vorhandenen Suiten schnell deaktivieren können, indem Sie einfach eine x
zur Funktion hinzufügen.
Spezifikationen innerhalb einer xdescribe()
-Funktion werden als ausstehend markiert und im Bericht nicht ausgeführt.
Specs
Eine Spezifikation deklariert einen Testfall, der zu einer Testsuite gehört. Dies geschieht durch Aufrufen der globalen Jasmine-Funktion it()
, die zwei Parameter verwendet, den Titel der Spezifikation (der die Logik beschreibt, die wir testen möchten) und eine Funktion, die den tatsächlichen Testfall implementiert.
Eine Spezifikation kann eine oder mehrere Erwartungen enthalten. Jede Erwartung ist einfach eine Assertion, die entweder true
oder false
. Damit die Spezifikation übergeben werden kann, müssen alle Erwartungen, die zur Spezifikation gehören, true
Andernfalls schlägt die Spezifikation fehl.
Fügen Sie in unserer String Utils Suite die folgenden Spezifikationen hinzu:
describe("String Utils", function() { it("should be able to lower case a string",function() { /*...*/ }); it("should be able to upper case a string",function() { /*...*/ }); it("should be able to confirm if a string contains a substring",function() { /*...*/ }); it("should be able repeat a string multiple times",function() { /*...*/ });});
Fügen Sie in unserer Basic Math Utils Suite einige Spezifikationen hinzu:
describe("Basic Math Utils", function() { it("should be able to tell if a number is even",function() { /*...*/ }); it("should be able to tell if a number is odd",function() { /*...*/ }); });
Fügen Sie für die Advanced Math Utils:
describe("Advanced Math Utils", function() { it("should be able to tell if a number is prime",function() { /*...*/ }); it("should be able to calculate the fibonacci of a number",function() { /*...*/ }); });
Wie man Spezifikationen ausschließt
Genau wie Suiten können Sie auch einzelne Spezifikationen ausschließen, indem Sie die xit()
-Funktion verwenden, die die it()
-Spezifikation vorübergehend deaktiviert und die Spezifikation als ausstehend markiert.
Erwartungen
Erwartungen werden mit der expect()
Funktion erstellt, die einen Wert namens actual annimmt (dies können Werte, Ausdrücke, Variablen, Funktionen oder Objekte usw. sein).). Erwartungen bilden die Spezifikation und werden zusammen mit Matcher-Funktionen (über Verkettung) verwendet, um zu definieren, was der Entwickler von einer bestimmten Codeeinheit erwartet.
Eine Matcher-Funktion vergleicht zwischen einem tatsächlichen Wert (übergeben an die expect()
-Funktion, mit der sie verkettet ist) und einem erwarteten Wert (direkt als Parameter an den Matcher übergeben) und gibt entweder true oder false zurück, was die Spezifikation entweder übergibt oder fehlschlägt.
Sie können die expect()
Funktion mit mehreren Matchern verketten. Um das boolesche Ergebnis eines Matchers zu negieren / invertieren, können Sie das Schlüsselwort not
bevor Sie den Matcher aufrufen.
Lassen Sie uns die Spezifikationen unseres Beispiels implementieren. Im Moment verwenden wir expect()
mit dem nothing()
Matcher, der Teil der integrierten Matcher ist, die wir später sehen werden. Dies wird alle Spezifikationen bestehen, da wir an dieser Stelle nichts erwarten.
describe("MyJSUtilities", function() {describe(">String Utils", function() { it("should be able to lower case a string",function() { expect().nothing(); }); it("should be able to upper case a string",function() { expect().nothing(); }); it("should be able to confirm if a string contains a substring",function() { expect().nothing(); }); it("should be able repeat a string multiple times",function() { expect().nothing(); }); });describe("Math Utils", function() { describe("Basic Math Utils", function() { it("should be able to tell if a number is even",function() { expect().nothing(); }); it("should be able to tell if a number is odd",function() { expect().nothing(); }); }); describe("Advanced Math Utils", function() { it("should be able to tell if a number is prime",function() { expect().nothing(); }); it("should be able to calculate the fibonacci of a number",function() { expect().nothing(); }); }); });});
Dies ist ein Screenshot der Ergebnisse an dieser Stelle:
Wir haben acht bestandene Spezifikationen und null Fehler.
Sie können entweder integrierte Matcher verwenden oder auch eigene benutzerdefinierte Matcher für Ihre spezifischen Anforderungen erstellen.
Integrierte Matcher
Jasmine bietet einen umfangreichen Satz integrierter Matcher. Lassen Sie uns einige der wichtigsten sehen:
-
toBe()
zum Testen auf Identität, -
toBeNull()
zum Testen aufnull
, -
toBeUndefined()/toBeDefined()
zum Testen aufundefined
/nichtundefined
, -
toBeNaN()
zum Testen auf NaN (Keine Zahl) -
toEqual()
zum Testen auf Gleichheit, -
toBeFalsy()/toBeTruthy()
zum Testen auf Falschheit/Wahrhaftigkeit usw.
Die vollständige Liste der Matcher finden Sie in den Dokumenten.
Lassen Sie uns nun unsere Spezifikationen gegebenenfalls mit einigen dieser Matcher implementieren. Importieren Sie zuerst die Funktionen, die wir testen, in unsere MyJSUtilitiesSpec.js
-Datei:
const utils = require("../index.js");
Beginnen Sie als nächstes mit der Zeichenfolge Utils suite und ändern Sie expect().nothing()
mit den entsprechenden Erwartungen.
Zum Beispiel erwarten wir für die erste Spezifikation, dass die toLowerCase()
Methode zuerst definiert wird und zweitens eine Kleinbuchstaben-Zeichenfolge zurückgibt, dh:
it("should be able to lower case a string",function() { expect(utils.toLowerCase).toBeDefined(); expect(utils.toLowerCase("HELLO WORLD")).toEqual("hello world"); });
Dies ist der vollständige Code für die Suite:
describe(">String Utils", function() { it("should be able to lower case a string",function() { expect(utils.toLowerCase).toBeDefined(); expect(utils.toLowerCase("HELLO WORLD")).toEqual("hello world"); }); it("should be able to upper case a string",function() { expect(utils.toUpperCase).toBeDefined(); expect(utils.toUpperCase("hello world")).toEqual("HELLO WORLD"); }); it("should be able to confirm if a string contains a substring",function() { expect(utils.contains).toBeDefined(); expect(utils.contains("hello world","hello",0)).toBeTruthy(); }); it("should be able repeat a string multiple times",function() { expect(utils.repeat).toBeDefined(); expect(utils.repeat("hello", 3)).toEqual("hellohellohello"); }); });
Benutzerdefinierte Matcher
Jasmine bietet die Möglichkeit, benutzerdefinierte Matcher zu schreiben, um Assertionen zu implementieren, die nicht von den integrierten Matchern abgedeckt werden, oder um Tests beschreibender und lesbarer zu machen.Nehmen wir zum Beispiel die folgende Spezifikation:
it("should be able to tell if a number is even",function() { expect(utils.isEven).toBeDefined(); expect(utils.isEven(2)).toBeTruthy(); expect(utils.isEven(1)).toBeFalsy(); });
Nehmen wir an, dass die isEven()
-Methode nicht implementiert ist. Wenn wir die Tests ausführen, erhalten wir Nachrichten wie den folgenden Screenshot:
Die Fehlermeldung, die wir erhalten, besagt, dass erwartet wird, dass undefiniert definiert wird, was uns keine Ahnung gibt, was passiert. Lassen Sie uns diese Nachricht im Kontext unserer Codedomäne aussagekräftiger gestalten (dies ist für komplexe Codebasen nützlicher). Lassen Sie uns in dieser Angelegenheit einen benutzerdefinierten Matcher erstellen.
Wir erstellen benutzerdefinierte Matcher mit der addMatchers()
-Methode, die ein Objekt verwendet, das aus einer oder mehreren Eigenschaften besteht, die als Matcher hinzugefügt werden. Jede Eigenschaft sollte eine Factory-Funktion bereitstellen, die zwei Parameter akzeptiert: util
, die eine Reihe von Utility-Funktionen für Matcher enthält (siehe: matchersUtil.js
) und customEqualityTesters
, die übergeben werden muss, wenn util.equals
aufgerufen wird, und sollte eine objekt mit einer compare
Funktion, die aufgerufen wird, um die Erwartung zu überprüfen.
Wir müssen den benutzerdefinierten Matcher registrieren, bevor wir jede Spezifikation mit der beforeEach()
-Methode ausführen:
describe("/Basic Math Utils", function () {beforeEach(function () {jasmine.addMatchers({hasEvenMethod: function (util, customEqualityTesters) {return {compare: function (actual, expected) {var result = { pass: utils.isEven !== undefined };if (result.pass) {result.message = "Expected isEven() to be not defined."}else {result.message = "Expected isEven() to be defined."}return result;}}}});});/*...*/});
Wir können dann den benutzerdefinierten Matcher anstelle von expect(utils.isEven).toBeDefined()
verwenden:
expect().hasEvenMethod();
Dadurch erhalten wir eine bessere Fehlermeldung:
Mit beforeEach() und afterEach()
Zum Initialisieren und Bereinigen Ihrer Spezifikationen bietet Jasmine zwei globale Funktionen, beforeEach()
und afterEach()
:
- Die
beforeEach
-Funktion wird einmal vor jeder Spezifikation in der Suite aufgerufen, in der sie aufgerufen wird. - Die
afterEach
Funktion wird einmal nach jeder Spezifikation in der Suite aufgerufen, in der sie aufgerufen wird.
Wenn Sie beispielsweise Variablen in Ihrer Testsuite verwenden müssen, können Sie diese einfach am Anfang der describe()
-Funktion deklarieren und einen beliebigen Initialisierungs- oder Instanziierungscode in eine beforeEach()
-Funktion einfügen. Schließlich können Sie die Funktion afterEach()
verwenden, um die Variablen nach jeder Spezifikation zurückzusetzen, sodass Sie reine Komponententests durchführen können, ohne den Initialisierungs- und Bereinigungscode für jede Spezifikation wiederholen zu müssen.
Die beforeEach()
-Funktion ist auch perfekt mit vielen Jasmine-APIs kombiniert, z. B. der addMatchers()
-Methode zum Erstellen benutzerdefinierter Matcher oder auch mit der done()
-Funktion, um auf asynchrone Vorgänge zu warten, bevor Sie mit dem Testen fortfahren.
Fehlschlagen eines Tests
Sie können einen Test mit der in Jasmine verfügbaren globalen fail()
-Methode zum Fehlschlagen zwingen. Zum Beispiel:
it("should explicitly fail", function () { fail('Forced to fail'); });
Sie sollten den folgenden Fehler erhalten:
Testen auf Ausnahmen
Wenn Sie Ihren Code Unit-testen, werden möglicherweise Fehler und Ausnahmen ausgelöst, sodass Sie möglicherweise auf diese Szenarien testen müssen. Jasmine bietet die Matcher toThrow()
und toThrowError()
, um zu testen, wann eine Ausnahme ausgelöst wird, bzw. um auf eine bestimmte Ausnahme zu testen.
Zum Beispiel, wenn wir eine Funktion haben, die eine TypeError
Ausnahme auslöst:
function throwsError() { throw new TypeError("A type error"); }
Sie könnten eine Spezifikation schreiben, die getestet werden soll, ob eine Ausnahme ausgelöst wird:
it('it should throw an exception', function () { expect(throwsError).toThrow(); });
Oder Sie könnten auch test für die spezifische TypeError
Ausnahme:
it('it should throw a TypeError', function () { expect(throwsError).toThrowError(TypeError); });
Spione verstehen
Meistens hängen Methoden von anderen Methoden ab. Dies bedeutet, dass Sie beim Testen einer Methode möglicherweise auch deren Abhängigkeiten testen. Dies wird beim Testen nicht empfohlen, dh Sie müssen sicherstellen, dass Sie die reine Funktion testen, indem Sie die Methode isolieren und sehen, wie sie sich bei einer Reihe von Eingaben verhält.
Jasmine bietet Spione, mit denen Methodenaufrufe von Objekten ausspioniert / abgehört und gemeldet werden können, ob eine Methode aufgerufen wird und mit welchem Kontext und welchen Argumenten.
Jasmine bietet zwei Möglichkeiten, Methodenaufrufe auszuspionieren: mit den spyOn()
oder den createSpy()
Methoden.
Sie können spyOn()
wenn die Methode bereits für das Objekt vorhanden ist, andernfalls müssen Sie jasmine.createSpy()
die eine neue Funktion zurückgibt.
Standardmäßig meldet ein Spion nur, wenn ein Anruf ohne Aufruf der spionierten Funktion (i.sie können das Standardverhalten jedoch mit den folgenden Methoden ändern:
-
and.callThrough()
: Aufruf über die ursprüngliche Funktion, -
and.returnValue(value)
: Geben Sie den angegebenen Wert zurück, -
and.callFake(fn)
: Rufen Sie die gefälschte Funktion anstelle der ursprünglichen eins, -
and.throwError(err)
: Wirft einen Fehler, -
and.stub()
: Setzt das Standard-Stubbing-Verhalten zurück.
Sie können einen Spion verwenden, um Laufzeitstatistiken über die spionierte Funktion zu sammeln, z. B. wenn Sie wissen möchten, wie oft Ihre Funktion aufgerufen wurde.
Angenommen, wir möchten sicherstellen, dass unsere toUpperCase()
-Methode die integrierte String.toUpperCase()
-Methode verwendet, müssen wir sie einfach ausspionieren String.toUpperCase()
mit:
it("should be able to upper case a string", function () {
var spytoUpperCase = spyOn(String.prototype, 'toUpperCase')
expect(utils.toUpperCase).toBeDefined(); expect(utils.toUpperCase("hello world")).toEqual("HELLO WORLD"); expect(String.prototype.toUpperCase).toHaveBeenCalled(); expect(spytoUpperCase.calls.count()).toEqual(1); });
Der Test ist aufgrund der zweiten Erwartung fehlgeschlagen, da utils.toUpperCase("hello world")
undefined anstelle der erwarteten HELLO WORLD zurückgegeben hat. Das liegt daran, dass, wie bereits erwähnt, die Methode nach dem Erstellen des Spions auf toUpperCase()
nicht ausgeführt wird. Wir müssen dieses Standardverhalten ändern, indem wir callThrough()
:
Bitte beachten Sie, dass eine
spy
Funktion die spionierte Funktion standardmäßig durch einen Stub ersetzt. Wenn Sie stattdessen die ursprüngliche Funktion aufrufen müssen, können Sie.and.callThrough()
zu Ihremspy
Objekt hinzufügen.
var spytoUpperCase = spyOn(String.prototype, 'toUpperCase').and.callThrough();
Jetzt sind alle Erwartungen erfüllt.
Sie können auch and.callFake()
oder and.returnValue()
, um entweder die ausspionierte Funktion oder nur den Rückgabewert zu fälschen, wenn Sie dies nicht tun Rufen Sie die eigentliche Funktion auf:
var spytoUpperCase = spyOn(String.prototype, 'toUpperCase').and.returnValue("HELLO WORLD");
var spytoUpperCase = spyOn(String.prototype, 'toUpperCase').and.callFake(function(){ return "HELLO WORLD"; });
Wenn wir nun die eingebaute String.toUpperCase()
in unserer eigenen utils.toUpperCase()
Implementierung nicht verwenden, erhalten wir diese Fehler:
Die beiden Erwartungen expect(String.prototype.toUpperCase).toHaveBeenCalled()
expect(spytoUpperCase.calls.count()).toEqual(1)
sind fehlgeschlagen.
Umgang mit Asynchronität in Jasmine
Wenn der Code, den Sie testen, asynchrone Vorgänge enthält, müssen Sie Jasmine mitteilen, wann die asynchronen Vorgänge abgeschlossen sind.
Standardmäßig wartet Jasmine darauf, dass eine asynchrone Operation, die durch einen Rückruf, ein Versprechen oder das Schlüsselwort async
definiert ist, abgeschlossen ist. Wenn Jasmine in einer dieser Funktionen ein Callback-, Promise- oder async-Schlüsselwort findet: beforeEach
afterEach
beforeAll
afterAll
und it
es wird für die asynchrone, bevor Sie mit der nächsten Operation durchgeführt werden.
Verwenden von done() mit beforeEach()/it() ..
Nehmen wir unser Beispiel simulateAsyncOp()
, das eine asynchrone Operation mit setTimeout()
simuliert. In einem realen Szenario kann dies eine Ajax-Anfrage oder ähnliches sein, die asynchron abläuft:
function simulateAsyncOp(callback){
setTimeout(function () { callback(); }, 2000); }
Um diese Funktion zu testen, können wir die beforeEach()
Funktion mit dem speziellen done()
Rückruf verwenden. Unser Code muss done()
aufrufen, um Jasmine mitzuteilen, dass der asynchrone Vorgang abgeschlossen ist:
describe("/Async Op", function () {var asyncOpCompleted = false;beforeEach(function (done) {utils.simulateAsyncOp(function(){ asyncOpCompleted = true; done();});});it("should be able to tell if the async call has completed", function () { expect(asyncOpCompleted).toEqual(true);});});
Wir können schnell einen Nachteil dieser Methode feststellen, daher müssen wir unseren Code schreiben, um den done()
Rückruf zu akzeptieren. In unserem Fall haben wir die done()
-Methode in unserer simulateAsyncOp(fn)
aber wir haben einen Callback-Parameter bereitgestellt, um done()
aufrufen zu können.
Verwenden von Versprechen
Wenn Sie keinen Code erstellen möchten, der davon abhängt, wie Sie Ihren Test schreiben, können Sie stattdessen ein Versprechen verwenden und den done()
Rückruf aufrufen, wenn das Versprechen aufgelöst wurde. Oder besser noch, in Jasmine 2.7+, wenn Ihr Code ein Promise
, wartet Jasmine, bis es aufgelöst oder abgelehnt wird, bevor der nächste Code ausgeführt wird.
async/ await verwenden
Jasmine 2.7+ unterstützt async
und await
Aufrufe in Spezifikationen. Dies entlastet Sie davon, Asserts in einen .then()
oder .catch()
Block zu setzen.
it("should work with async/await", async () => { let completed = false; completed = await utils.simulateAsyncOp(); expect(completed).toEqual(true); });
Dies ist die Implementierung von simulateAsyncOp
:
function simulateAsyncOp() {
return new Promise(resolve => { setTimeout(() => { resolve(true); }, 1000); }); }
Verwendung der Jasmine-Uhr
Die Jasmine-Uhr wird zum Testen von asynchronem Code verwendet, der von Zeitfunktionen abhängt, z. B. setTimeout()
Auf die gleiche Weise testen wir synchronen Code, indem wir zeitbasierte APIs mit benutzerdefinierten Methoden verspotten. Auf diese Weise können Sie die getesteten Funktionen synchron ausführen, indem Sie die Uhr steuern oder manuell vorschieben.
Sie können die Jasmine-Uhr installieren, indem Sie die Funktion jasmine.clock().install
in Ihrer Spezifikation oder Suite aufrufen.
Nachdem Sie die Uhr verwendet haben, müssen Sie sie deinstallieren, um die ursprünglichen Funktionen wiederherzustellen.
Mit Jasmine clock können Sie die JavaScript-Funktionen setTimeout
oder setInterval
steuern, indem Sie die Uhr ankreuzen, um mit der Funktion jasmine.clock().tick
in der Zeit voranzukommen, die die Anzahl der Millisekunden benötigt, mit denen Sie sich bewegen können.
Sie können auch die Jasmin-Uhr verwenden, um das aktuelle Datum zu verspotten.
beforeEach(function () {jasmine.clock().install();});afterEach(function() {jasmine.clock().uninstall();});it("should call the asynchronous operation synchronously", function() {var completed = false;utils.simulateAsyncOp(function(){completed = true;});expect(completed).toEqual(false);jasmine.clock().tick(1001);expect(completed).toEqual(true);});
Dies ist die simulateAsyncOp
Funktion:
function simulateAsyncOp(callback){
setTimeout(function () { callback(); }, 1000); }
Falls Sie keine Zeit für die Funktion
mockDate
angegeben haben, wird das aktuelle Datum verwendet.
Fehlerbehandlung
Wenn Ihr asynchroner Code aufgrund eines Fehlers fehlschlägt, möchten Sie, dass Ihre Spezifikationen korrekt fehlschlagen. Ab Jasmine 2.6+ werden alle unbehandelten Fehler an die aktuell ausgeführte Spezifikation gesendet.
Jasmine bietet auch eine Möglichkeit, die Sie verwenden können, wenn Sie Ihre Spezifikationen explizit nicht erfüllen müssen:
- mit dem
done()
Callback mitbeforeEach()
durch Aufruf derdone.fail(err)
Methode, - einfach einen Fehler an den
done(err)
Callback (Jasmine 3+), - Aufruf der
reject()
Methode einesPromise
.
Fazit
In diesem Handbuch haben wir Jasmine vorgestellt und gezeigt, wie Sie mit Jasmine beginnen können, um Ihren JavaScript-Code zu testen. Danke fürs Lesen!
Dieser Artikel wurde ursprünglich in techiediaries veröffentlicht.