Unit Tests

Nachdem Sie Code geändert oder hinzugefügt haben, sollten Sie vorhandene Unit-Tests ausführen und weitere schreiben. Alle Tests werden mit den nicht komprimierten Codeversionen ausgeführt.

Es gibt zwei Sätze von Einheitentests: JS-Tests und Blockgenerator-Tests.

JS-Tests

Mit den JS-Tests wird der Betrieb interner JavaScript-Funktionen im Kern von Blockly bestätigt. Wir verwenden Mocha zum Ausführen von Einheitentests, Sinon zum Erstellen von Stub-Abhängigkeiten und Chai, um Assertions für den Code zu erstellen.

Laufende Tests

Sowohl in Blockly- als auch in Blockly-Stichproben führt npm run test die Einheitentests aus. In Blockly werden dadurch auch andere Tests wie Lint und Kompilierung ausgeführt. Sie können tests/mocha/index.html auch in einem Browser öffnen, um alle Mocha-Tests interaktiv auszuführen.

Tests schreiben

Wir verwenden die Mocha-TDD-Oberfläche, um Tests auszuführen. Tests sind in Suiten organisiert, die sowohl zusätzliche Untergruppen als auch Tests enthalten können. Im Allgemeinen hat jede Blockly-Komponente (z. B. toolbox oder workspace) eine eigene Testdatei, die eine oder mehrere Suiten enthält. Jede Suite kann eine setup- und eine teardown-Methode haben, die vor bzw. nach jedem Test in dieser Suite aufgerufen werden.

Testhilfen

Es gibt eine Reihe von Blockly-spezifischen Hilfsfunktionen, die beim Schreiben von Tests nützlich sein können. Sie finden sie in core und in blockly-samples.

Zu den Hilfsfunktionen gehören sharedTestSetup und sharedTestTeardown, die vor und nach den Tests aufgerufen werden müssen (siehe Abschnitt „Anforderungen“).

sharedTestSetup:
  • Richtet imaginäre Timer ein (in einigen Tests müssen Sie this.clock.runAll verwenden).
  • Stubs für Blockly.Events.fire, die sofort ausgelöst werden (konfigurierbar).
  • Hiermit wird die automatische Bereinigung von blockTypes eingerichtet, die über defineBlocksWithJsonArray definiert wurden.
  • Hier werden einige Properties für den this-Kontext deklariert, die zugänglich sein sollen:
    • this.clock (sollte aber nicht wiederhergestellt werden, da dies Probleme in sharedTestTeardown verursachen kann)
    • this.eventsFireStub
    • this.sharedCleanup (mit addMessageToCleanup und addBlockTypeToCleanup verwenden) (HINWEIS: Sie müssen addBlockTypeToCleanup nicht verwenden, wenn Sie den Block mit defineBlocksWithJsonArray definiert haben)

Die Funktion hat einen optionalen Parameter options zum Konfigurieren der Einrichtung. Derzeit wird es nur verwendet, um zu bestimmen, ob Blockly.Events.fire sofort ausgelöst werden soll (standardmäßig wird ein Stub erstellt).

sharedTestTeardown:
  • Löscht den Arbeitsbereich this.workspace (je nachdem, wo er definiert wurde, siehe Abschnitt zu Testanforderungen).
  • Stellt alle Stubs wieder her.
  • Alle Blocktypen werden entfernt, die über defineBlocksWithJsonArray und addBlockTypeToCleanup hinzugefügt wurden.
  • Alle Nachrichten, die über addMessageToCleanup hinzugefügt wurden, werden bereinigt.

Testanforderungen

  • Jeder Test muss sharedTestSetup.call(this); als erste Zeile in der Einrichtung der äußersten Suite und sharedTestTeardown.call(this); als letzte Zeile in der Deaktivierung der äußersten Suite für eine Datei aufrufen.
  • Wenn Sie einen Arbeitsbereich mit einer generischen Toolbox benötigen, können Sie eine der vordefinierten Toolboxen auf dem Test-index.html verwenden. Unten finden Sie ein Beispiel.
  • Sie müssen this.workspace ordnungsgemäß entsorgen. In den meisten Tests definieren Sie this.workspace in der äußersten Suite und verwenden sie für alle nachfolgenden Tests. In einigen Fällen können Sie sie jedoch auch in einer inneren Suite definieren oder neu definieren (z. B. erfordert für einen Ihrer Tests ein Arbeitsbereich mit anderen Optionen als ursprünglich eingerichtet). Sie muss am Ende des Tests entsorgt werden.
    • Wenn Sie this.workspace in der äußersten Suite definieren und nie neu definieren, sind keine weiteren Maßnahmen erforderlich. Sie wird automatisch am sharedTestTeardown entsorgt.
    • Wenn Sie this.workspace zum ersten Mal in einer inneren Suite definieren (d.h. nicht in der äußersten Suite), müssen Sie es manuell beseitigen, indem Sie workspaceTeardown.call(this, this.workspace) beim Abbau dieser Suite aufrufen.
    • Wenn Sie this.workpace in der äußersten Suite definieren, sie aber dann in einer inneren Testsuite neu definieren, müssen Sie zuerst workspaceTeardown.call(this, this.workspace) aufrufen, bevor Sie sie neu definieren, um den ursprünglichen Arbeitsbereich in der Suite der obersten Ebene zu entfernen. Sie müssen den neuen Wert auch manuell entsorgen, indem Sie workspaceTeardown.call(this, this.workspace) noch einmal beim Abbau dieser inneren Suite aufrufen.

Teststruktur

Unittests folgen in der Regel einer festgelegten Struktur, die sich zusammenfassen lässt als Anordnung, Ausführung, Bestätigung.

  1. Anordnen: Legen Sie den Status der Welt und alle erforderlichen Bedingungen für das zu testende Verhalten fest.
  2. Act (Aktion): Rufen Sie den zu testenden Code auf, um das zu testende Verhalten auszulösen.
  3. Assert: Sie können Behauptungen über den Rückgabewert oder Interaktionen mit simulierten Objekten aufstellen, um die Richtigkeit zu überprüfen.

Bei einem einfachen Test gibt es möglicherweise kein Verhalten, das eingerichtet werden muss. Die Phasen „Aktion“ und „Bestätigung“ können kombiniert werden, indem der Aufruf des zu testenden Codes in die Bestätigung eingefügt wird. In komplexeren Fällen sind Ihre Tests besser lesbar, wenn Sie diese drei Phasen einhalten.

Hier sehen Sie eine Beispieltestdatei, die vereinfacht ist.

suite('Flyout', function() {
  setup(function() {
    sharedTestSetup.call(this);
    this.toolboxXml = document.getElementById('toolbox-simple');
    this.workspace = Blockly.inject('blocklyDiv',
        {
          toolbox: this.toolboxXml
        });
  });

  teardown(function() {
    sharedTestTeardown.call(this);
  });

  suite('simple flyout', function() {
    setup(function() {
      this.flyout = this.workspace.getFlyout();
    });
    test('y is always 0', function() {
      // Act and assert stages combined for simple test case
      chai.assert.equal(this.flyout.getY(), 0, 'y coordinate in vertical flyout is 0');
    });
    test('x is right of workspace if flyout at right', function() {
      // Arrange
      sinon.stub(this.flyout.targetWorkspace, 'getMetrics').returns({
        viewWidth: 100,
      });
      this.flyout.targetWorkspace.toolboxPosition = Blockly.TOOLBOX_AT_RIGHT;
      this.flyout.toolboxPosition_ = Blockly.TOOLBOX_AT_RIGHT;

      // Act
      var x = this.flyout.getX();

      // Assert
      chai.assert.equal(x, 100, 'x is right of workspace');
    });
  });
});

Hinweise zu diesem Beispiel:

  • Eine Suite kann andere Suiten mit zusätzlichen setup- und teardown-Methoden enthalten.
  • Jede Suite und jeder Test hat einen aussagekräftigen Namen.
  • Chai-Assertions werden verwendet, um Assertions für den Code zu erstellen.
    • Sie können ein optionales Stringargument angeben, das angezeigt wird, wenn der Test fehlschlägt. So lassen sich fehlerhafte Tests leichter beheben.
    • Die Reihenfolge der Parameter ist chai.assert.equal(actualValue, expectedValue, optionalMessage). Wenn Sie actual und expected vertauschen, haben die Fehlermeldungen keinen Sinn.
  • Sinon wird für Stub-Methoden verwendet, wenn der echte Code nicht aufgerufen werden soll. In diesem Beispiel soll nicht die echte Messwertfunktion aufgerufen werden, da sie für diesen Test nicht relevant ist. Uns geht es nur darum, wie die Ergebnisse von der getesteten Methode verwendet werden. Sinon führt einen Stub für die getMetrics-Funktion ein, um eine Antwortvorlage zurückzugeben, die wir leicht in unseren Test-Assertions finden können.
  • Die setup-Methoden für jede Suite sollten nur eine allgemeine Einrichtung enthalten, die für alle Tests gilt. Wenn ein Test für ein bestimmtes Verhalten von einer bestimmten Bedingung abhängt, sollte diese Bedingung im entsprechenden Test klar angegeben sein.

Tests zur Fehlerbehebung

  • Sie können die Tests in einem Browser öffnen und mit den Entwicklertools Haltestellen festlegen, um zu prüfen, ob Ihre Tests unerwartet fehlschlagen (oder unerwartet bestehen).
  • Legen Sie für einen Test oder eine Suite .only() oder .skip() fest, um nur diesen Test bzw. diese Suite auszuführen oder einen Test zu überspringen. Beispiel:

    suite.only('Workspace', function () {
      suite('updateToolbox', function () {
        test('test name', function () {
          // ...
        });
        test.skip('test I don’t care about', function () {
          // ...
        });
      });
    });
    

    Denken Sie daran, diese zu entfernen, bevor Sie Ihren Code committen.

Blockgeneratortests

Für jeden Block gibt es eigene Unit-Tests. Bei diesen Tests wird überprüft, ob Blöcke Code generieren, der wie vorgesehen funktioniert.

  1. Lade tests/generators/index.html in Firefox oder Safari. Chrome und Opera haben Sicherheitseinschränkungen, die das Laden der Tests aus dem lokalen „file://“-System verhindern (Probleme 41024 und 47416).
  2. Wählen Sie im Drop-down-Menü den entsprechenden Teil des Systems aus, den Sie testen möchten, und klicken Sie auf „Laden“. Blöcke sollten im Arbeitsbereich angezeigt werden.
  3. Klicken Sie auf „JavaScript“.
    Kopieren Sie den generierten Code und führen Sie ihn in einer JavaScript-Konsole aus. Wenn die Ausgabe mit „OK“ endet, ist der Test bestanden.
  4. Klicken Sie auf „Python“.
    Kopieren Sie den generierten Code und führen Sie ihn in einem Python-Interpreter aus. Wenn die Ausgabe mit „OK“ endet, war der Test erfolgreich.
  5. Klicken Sie auf "PHP".
    Kopieren Sie den generierten Code und führen Sie ihn in einem PHP-Interpreter aus. Wenn die Ausgabe mit „OK“ endet, war der Test erfolgreich.
  6. Klicke auf „Lua“.
    Kopieren Sie den generierten Code und führen Sie ihn in einem Lua-Interpreter aus. Wenn die Ausgabe mit „OK“ endet, war der Test erfolgreich.
  7. Klicken Sie auf „Dart“.
    Kopieren Sie den generierten Code und führen Sie ihn in einem Dart-Interpreter aus. Wenn die Ausgabe mit „OK“ endet, war der Test erfolgreich.

Blockgenerator-Tests bearbeiten

  1. Laden Sie tests/generators/index.html in einem Browser.
  2. Wählen Sie im Drop-down-Menü den entsprechenden Teil des Systems aus und klicken Sie auf „Laden“. Blöcke sollten im Arbeitsbereich angezeigt werden.
  3. Nehmen Sie alle gewünschten Änderungen oder Ergänzungen an den Blöcken vor.
  4. Klicken Sie auf „XML“.
  5. Kopieren Sie die generierte XML-Datei in die entsprechende Datei in tests/generators/.