Testen von JavaScript-Code mit Jest auf Dummies. Teil 1

Hallo Habr! Dieser Leitfaden ist der erste Teil einer geplanten Artikelserie über ein so großartiges Test-Framework wie Jest. Das Material ist nützlich für Anfänger und diejenigen, die sich gerade erst mit dem Testen vertraut machen und dieses Framework studieren möchten. Im ersten Teil werden wir diskutieren: wie man mit Scherz anfängt, wie man einen einfachen Test schreibt und welche Methoden es gibt, um die zu prüfenden Werte mit den erwarteten zu vergleichen. Wen kümmert es - willkommen bei kat!

Was ist Scherz?


Wie auf der Projekthomepage angegeben:
Jest ist eine erstaunliche JavaScript-Testumgebung mit Schwerpunkt auf Einfachheit.
Und in der Tat ist Jest sehr einfach. Es erfordert keine zusätzlichen Einstellungen, ist leicht zu verstehen und zu verwenden und verfügt auch über eine ziemlich gute Dokumentation. Ideal für Projekte mit Node, React, Angular, Vue, Babel, TypeScript und mehr.
Es ist auch Open Source und wird von Facebook unterstützt.

Installation


Gehen Sie wie folgt vor, um Jest in Ihrem Projekt zu installieren:

npm install --save-dev jest

Wenn Sie Garn verwenden:

yarn add --dev jest

Nach der Installation können Sie den Skriptabschnitt Ihrer package.json aktualisieren:

“scripts” : {
     “test”: “jest”
}

Mit diesem einfachen Aufruf können wir unsere Tests bereits ausführen (für Scherz muss mindestens ein Test vorhanden sein).

Sie können auch global installieren (dies würde ich jedoch nicht empfehlen, da die globale Installation von Modulen für mich eine schlechte Praxis ist):

npm install jest --global

Und dementsprechend für Garn:

yarn global add jest

Danach können Sie Jest direkt über die Befehlszeile verwenden.

Wenn Sie den Befehl jest --init im Stammverzeichnis des Projekts aufrufen und nach Beantwortung einiger Fragen die Einstellungsdatei jest.config.js abrufen. Oder Sie können die Konfiguration direkt zu package.json hinzufügen. Fügen Sie dazu den Schlüssel "jest" zum json-Stammverzeichnis hinzu und fügen Sie im entsprechenden Objekt die erforderlichen Einstellungen hinzu. Wir werden die Optionen später selbst analysieren. Dies ist zu diesem Zeitpunkt nicht erforderlich, da der Scherz ohne zusätzliche Konfigurationen sofort verwendet werden kann.

Erster Test


Lassen Sie uns die Datei first.test.js erstellen und unseren ersten Test schreiben:

//first.test.js
test('My first test', () => {
    expect(Math.max(1, 5, 10)).toBe(10);
});

Führen Sie unsere Tests mit npm run test oder direkt mit dem Befehl jest aus (falls global installiert). Nach dem Start wird ein Bericht über das Bestehen der Tests angezeigt.

 <b>PASS</b>  ./first.test.js
  ✓ My first test (1 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        0.618 s, estimated 1 s

Lassen Sie uns unseren Test abbrechen und erneut scherzen:

//first.test.js
test('My first test', () => {
    expect(Math.max(1, 5, 10)).toBe(5);
});

Wie wir sehen können, besteht unser Test den Test jetzt nicht. Jest zeigt detaillierte Informationen darüber an, wo das Problem aufgetreten ist, was das erwartete Ergebnis war und was wir stattdessen erhalten haben.

Lassen Sie uns nun den Code des Tests selbst analysieren. Testfunktionwird verwendet, um einen neuen Test zu erstellen. Es werden drei Argumente benötigt (im Beispiel haben wir einen Aufruf mit zwei Argumenten verwendet). Die erste ist eine Zeile mit dem Namen des Tests, dessen Scherz im Bericht angezeigt wird. Die zweite ist eine Funktion, die die Logik unseres Tests enthält. Sie können auch das dritte Argument verwenden - Timeout. Es ist optional und der Standardwert beträgt 5 Sekunden. Es wird in Millisekunden eingestellt. Dieser Parameter ist erforderlich, wenn wir mit asynchronem Code arbeiten und ein Versprechen von der Testfunktion zurückgeben. Es gibt an, wie lange der Scherz warten muss, bis sich das Versprechen aufgelöst hat. Wenn das Versprechen nach dieser Zeit nicht eingehalten wurde, wird der Test als fehlgeschlagen angesehen. Weitere Informationen zum Arbeiten mit asynchronen Aufrufen finden Sie in den folgenden Abschnitten. Alternativ können Sie es () anstelle von test () verwenden.. Es gibt keinen Unterschied zwischen solchen Anrufen. it () ist nur ein Alias ​​für die Funktion test () .

Innerhalb der Testfunktion rufen wir zuerst require () auf . An ihn geben wir den Wert weiter, den wir überprüfen möchten. In unserem Fall ist dies das Ergebnis des Aufrufs von Math.max (1, 5, 10) . erwarteten () gibt ein Wrapper-Objekt zurück, das eine Reihe von Methoden zum Vergleichen des resultierenden Werts mit dem erwarteten Wert enthält. Eine dieser Methoden, die wir verwendet haben, ist toBe .

Schauen wir uns die wichtigsten dieser Methoden an:

  • toBe() — , , . Object.is(). === 0 -0, NaN c NaN.
  • toEqual() — , . . . .

    test('toEqual with objects', () => {
        expect({ foo: 'foo', subObject: { baz: 'baz' } })
            .toEqual({ foo: 'foo', subObject: { baz: 'baz' } });  // 
        expect({ foo: 'foo', subObject: { num: 0 } })
            .toEqual({ foo: 'foo', subObject: { baz: 'baz' } });  //    .
    });
    
    test('toEqual with arrays', () => {
        expect([11, 19, 5]).toEqual([11, 19, 5]); // 
        expect([11, 19, 5]).toEqual([11, 19]); // 
    });
    

  • toContain() — . ===.

    const arr = ['apple', 'orange', 'banana'];
    expect(arr).toContain('banana');
    expect(new Set(arr)).toContain('banana');
    expect('apple, orange, banana').toContain('banana');
    

  • toContainEqual() — .

    expect([{a: 1}, {b: 2}]).toContainEqual({a: 1});
    

  • toHaveLength() — length .

    expect([1, 2, 3, 4]).toHaveLength(4);
    expect('foo').toHaveLength(3);
    expect({ length: 1 }).toHaveLength(1);
    

  • toBeNull() — null.
  • toBeUndefined() — undefined.
  • toBeDefined()toBeUndefined. !== undefined.
  • toBeTruthy() — true. false, null, undefined, 0, NaN .
  • toBeFalsy()toBeTruthy(). false.
  • toBeGreaterThan() toBeGreaterThanOrEqual() — , >, >=.
  • toBeLessThan() toBeLessThanOrEqual()toBeGreaterThan() toBeGreaterThanOrEqual()
  • toBeCloseTo() — , , . .

    const num = 0.1 + 0.2; // 0.30000000000000004
    expect(num).toBeCloseTo(0.3);
    expect(Math.PI).toBeCloseTo(3.14, 2);
    

  • toMatch() — .

    expect('Banana').toMatch(/Ba/);
    

  • toThrow () - wird verwendet, wenn die Ausnahme überprüft werden muss. Sie können sowohl die Tatsache des Fehlers selbst als auch das Auslösen von Ausnahmen einer bestimmten Klasse überprüfen, entweder anhand der Fehlermeldung oder anhand der Entsprechung der Nachricht mit dem regulären Ausdruck.

    function funcWithError() {
        throw new Error('some error');
    }   
    expect(funcWithError).toThrow();
    expect(funcWithError).toThrow(Error);
    expect(funcWithError).toThrow('some error');
    expect(funcWithError).toThrow(/some/);
    

  • nicht - Mit dieser Eigenschaft können Sie nach Ungleichheit suchen. Es stellt ein Objekt bereit, das alle oben aufgeführten Methoden enthält, aber umgekehrt funktioniert.

    expect(true).not.toBe(false);
    expect({ foo: 'bar' }).not.toEqual({});
    
    function funcWithoutError() {}
    expect(funcWithoutError).not.toThrow();
    

Lassen Sie uns ein paar einfache Tests schreiben. Erstellen Sie zunächst ein einfaches Modul, das verschiedene Methoden zum Arbeiten mit Kreisen enthält.

// src/circle.js
const area = (radius) => Math.PI * radius ** 2;
const circumference = (radius) => 2 * Math.PI * radius;
module.exports = { area, circumference };

Fügen Sie als Nächstes die Tests hinzu:

// tests/circle.test.js
const circle = require('../src/circle');

test('Circle area', () => {
    expect(circle.area(5)).toBeCloseTo(78.54);
    expect(circle.area()).toBeNaN();
});

test('Circumference', () => {
    expect(circle.circumference(11)).toBeCloseTo(69.1, 1);
    expect(circle.circumference()).toBeNaN();
});

In diesen Tests haben wir das Ergebnis von 2 Methoden überprüft - Fläche und Umfang . Mit der toBeCloseTo- Methode haben wir das erwartete Ergebnis überprüft. Im ersten Fall haben wir geprüft, oder die berechnete Fläche des Kreises mit einem Radius von 5 entspricht ungefähr 78,54, während die Differenz zum erhaltenen Wert (78,53981633974483) nicht groß ist und der Test gezählt wird. Im zweiten haben wir angegeben, dass wir daran interessiert sind, auf 1 Dezimalstelle zu prüfen. Wir haben unsere Methoden auch ohne Argumente aufgerufen und das Ergebnis mit toBeNaN überprüft . Da das Ergebnis ihrer Ausführung NaN ist, werden die Tests erfolgreich bestanden.

Nehmen wir ein anderes Beispiel. Erstellen wir eine Funktion, die eine Reihe von Produkten nach Preis filtert:

// src/productFilter.js
const byPriceRange = (products, min, max) =>
         products.filter(item => item.price >= min && item.price <= max);
module.exports = { byPriceRange };

Und fügen Sie einen Test hinzu:

// tests/product.test.js
const productFilter = require('../src/producFilter');

const products = [
    { name: 'onion', price: 12 },
    { name: 'tomato', price: 26 },
    { name: 'banana', price: 29 },
    { name: 'orange', price: 38 }
];

test('Test product filter by range', () => {
    const FROM = 15;
    const TO = 30;
    const filteredProducts = productFilter.byPriceRange(products, FROM, TO);

    expect(filteredProducts).toHaveLength(2);
    expect(filteredProducts).toContainEqual({ name: 'tomato', price: 26 });
    expect(filteredProducts).toEqual([{ name: 'tomato', price: 26 }, { name: 'banana', price: 29 }]);
    expect(filteredProducts[0].price).toBeGreaterThanOrEqual(FROM);
    expect(filteredProducts[1].price).toBeLessThanOrEqual(TO);
    expect(filteredProducts).not.toContainEqual({ name: 'orange', price: 38 });
});

In diesem Test überprüfen wir das Ergebnis der Funktion byRangePrice . Zuerst haben wir die Entsprechung der Länge des resultierenden Arrays mit dem erwarteten - 2 überprüft. Die nächste Überprüfung erfordert, dass das Element das Element enthält - {Name: 'Tomate', Preis: 26}. Das Objekt im Array und das an ContainEqual übergebene Objekt sind zwei verschiedene Objekte, keine Verweise auf dasselbe. Aber toContainEqual wird jede Eigenschaft vergleichen. Da beide Objekte identisch sind, ist die Überprüfung erfolgreich. Als nächstes verwenden wir toEqual , um die Struktur des gesamten Arrays und seiner Elemente zu überprüfen. Mit den Methoden toBeGreaterThanOrEqual und toBeLessThanOrEqual können wir den Preis des ersten und zweiten Elements des Arrays überprüfen. Und schließlich not.toContainEqual aufrufenprüft, ob das Element im Array enthalten ist - {name: 'orange', price: 38}, das unter bestimmten Bedingungen nicht vorhanden sein sollte.

In diesen Beispielen haben wir einige einfache Tests mit den oben beschriebenen Verifizierungsfunktionen geschrieben. In den folgenden Abschnitten werden wir diskutieren, wie man mit asynchronem Code arbeitet, welche Scherzfunktionen in diesem Teil des Tutorials nicht behandelt wurden, wie man ihn konfiguriert und vieles mehr.

All Articles