Unit Tests

Nachdem Sie Code geändert oder hinzugefügt haben, sollten Sie vorhandene Einheitentests ausführen und erwägen, weitere zu schreiben. Alle Tests werden für die unkomprimierten Versionen des Codes ausgeführt.

Es gibt zwei Arten von Einheitentests: JS-Tests und Blockgenerator-Tests.

JS-Tests

Die JS-Tests bestätigen die Funktionsweise interner JavaScript-Funktionen im Blockly-Kern. Wir verwenden Mocha, um Einheitentests auszuführen, mit Sinon Abhängigkeiten per Stub und mit Chai Assertions zum Code.

Laufende Tests

Sowohl in blockly- als auch blockly-Samples führt npm run test die Einheitentests aus. Dabei werden im Block auch andere Tests wie Linting und Kompilierung ausgeführt. Sie können tests/mocha/index.html auch in einem Browser öffnen, um alle Mocha-Tests interaktiv auszuführen.

Schreiben von Tests

Wir verwenden die Mocha TDD-Schnittstelle, um Tests durchzuführen. Tests sind in Suiten organisiert, die sowohl zusätzliche Untersuites als auch Tests enthalten können. Im Allgemeinen hat jede Komponente von Blockly (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 wird.

Testhelfer

Es gibt eine Reihe spezieller Hilfsfunktionen für Blockly, die beim Schreiben von Tests nützlich sein können. Diese finden Sie in core und in blockly-Samples.

Zu den Hilfsfunktionen gehören sharedTestSetup und sharedTestTeardown, die vor und nach den Tests erforderlich sind (siehe Abschnitt „Anforderungen“).

sharedTestSetup:
  • Richtet fiktive Sinon-Timer ein (in einigen Tests müssen Sie this.clock.runAll verwenden).
  • Stubs: Blockly.Events.fire zur sofortigen Auslösung (konfigurierbar)
  • Richtet die automatische Bereinigung der über defineBlocksWithJsonArray definierten blockTypes ein.
  • Deklariert einige Attribute im this-Kontext, die zugänglich sein sollen:
    • this.clock (sollte aber nicht wiederhergestellt werden, da sonst Probleme in sharedTestTeardown auftreten)
    • this.eventsFireStub
    • this.sharedCleanup (zur Verwendung mit addMessageToCleanup und addBlockTypeToCleanup) (HINWEIS: Sie müssen addBlockTypeToCleanup nicht verwenden, wenn Sie den Block mit defineBlocksWithJsonArray definiert haben.)

Für die Konfiguration der Funktion gibt es einen optionalen options-Parameter. Derzeit wird damit nur festgestellt, ob Blockly.Events.fire sofort ausgelöst werden soll (standardmäßig wird ein Stub).

sharedTestTeardown:
  • Beseitigt den Arbeitsbereich this.workspace (je nachdem, wo er definiert wurde. Weitere Informationen finden Sie im Abschnitt „Testanforderungen“).
  • Stellt alle Stubs wieder her.
  • bereinigt alle Blocktypen, die über defineBlocksWithJsonArray und addBlockTypeToCleanup hinzugefügt wurden.
  • Es werden alle über addMessageToCleanup hinzugefügten Nachrichten bereinigt.

Testanforderungen

  • Bei jedem Test muss sharedTestSetup.call(this); als erste Zeile in der Einrichtung der äußersten Suite und sharedTestTeardown.call(this); als letzte Zeile im Teardown der äußersten Suite für eine Datei aufgerufen werden.
  • Wenn Sie einen Arbeitsbereich mit einer generischen Toolbox benötigen, können Sie eine der voreingestellten Toolboxen für die Test-index.html verwenden. Unten sehen 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 ihn jedoch auch in einer inneren Suite definieren oder neu definieren (z. B. erfordert einer Ihrer Tests einen Arbeitsbereich mit anderen Optionen als ursprünglich eingerichtet). Es 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. Es wird von sharedTestTeardown automatisch entsorgt.
    • Wenn Sie this.workspace zum ersten Mal in einer inneren Suite definieren (d.h., Sie haben ihn nicht in der äußeren Suite definiert), müssen Sie ihn manuell entsorgen, indem Sie workspaceTeardown.call(this, this.workspace) im Teardown dieser Suite aufrufen.
    • Wenn Sie this.workpace in der äußersten Suite definieren, sie dann aber in einer inneren Testsuite neu definieren, müssen Sie zuerst workspaceTeardown.call(this, this.workspace) aufrufen, bevor Sie die Datei neu definieren, um den ursprünglichen Arbeitsbereich zu entfernen, der in der Suite auf oberster Ebene definiert wurde. Sie müssen den neuen Wert auch manuell verwerfen. Rufen Sie dazu workspaceTeardown.call(this, this.workspace) im Teardown dieser inneren Suite noch einmal auf.

Teststruktur

Einheitentests folgen im Allgemeinen einer festgelegten Struktur, die als arrange, action, assert. zusammengefasst werden kann.

  1. Anordnen: Legen Sie den Zustand der Welt und alle erforderlichen Bedingungen für das zu testende Verhalten fest.
  2. Handeln: Rufen Sie den zu testenden Code auf, um das zu testende Verhalten auszulösen.
  3. Assert: Übernehmen Sie Assertions zum Rückgabewert oder zu Interaktionen mit Mock-Objekten, um deren Richtigkeit zu überprüfen.

Bei einem einfachen Test gibt es möglicherweise kein Verhalten zu arrangieren. Die Phasen "act" und "assert" können kombiniert werden, indem der Aufruf an den zu testenden Code in der Assertion eingebettet wird. Bei komplexeren Fällen sind Ihre Tests besser lesbar, wenn Sie sich an diese drei Phasen halten.

Hier sehen Sie eine beispielhafte Testdatei (vereinfacht dargestellt).

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');
    });
  });
});

Beachten Sie bei diesem Beispiel Folgendes:

  • 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 zum Code zu machen.
    • Sie können ein optionales Stringargument angeben, das angezeigt wird, wenn der Test fehlschlägt. Dies erleichtert das Debuggen fehlerhafter Tests.
    • Die Reihenfolge der Parameter ist chai.assert.equal(actualValue, expectedValue, optionalMessage). Wenn Sie actual und expected vertauschen, ergeben die Fehlermeldungen keinen Sinn.
  • Sinon wird für Stub-Methoden verwendet, wenn Sie nicht den echten Code aufrufen möchten. In diesem Beispiel soll die Funktion für echte Messwerte nicht aufgerufen werden, da sie für diesen Test nicht relevant ist. Es ist nur wichtig, wie die Ergebnisse von der zu testenden Methode verwendet werden. Sinon-Stubs der getMetrics-Funktion, um eine vorformulierte Antwort zurückzugeben, die wir in unseren Test-Assertions leicht prüfen können.
  • Die setup-Methoden für die einzelnen Suite sollten nur allgemeine Einstellungen enthalten, die für alle Tests gelten. Wenn ein Test für ein bestimmtes Verhalten auf einer bestimmten Bedingung basiert, sollte diese Bedingung im entsprechenden Test klar angegeben werden.

Debugging-Tests

  • Sie können die Tests in einem Browser öffnen und die Entwicklertools verwenden, um Haltepunkte festzulegen und zu prüfen, ob Ihre Tests unerwartet fehlschlagen oder unerwartet fehlschlagen.
  • Legen Sie .only() oder .skip() für einen Test oder eine Suite so fest, dass nur diese Gruppe von Tests ausgeführt wird, oder überspringen Sie einen Test. 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 vor dem Anwenden des Codes zu entfernen.

Blockgenerator-Tests

Jeder Block hat seine eigenen Einheitentests. Mit diesen Tests wird geprüft, ob Blöcke Code generieren, als er wie vorgesehen funktioniert.

  1. Lade tests/generators/index.html in Firefox oder Safari. Für Chrome und Opera gelten Sicherheitsbeschrä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 zu testenden Teil des Systems aus und klicken Sie auf „Laden“. Die Blöcke sollten im Arbeitsbereich angezeigt werden.
  3. Klicke auf „JavaScript“.
    Kopiere den generierten Code und führe 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, ist der Test bestanden.
  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, ist der Test bestanden.
  6. Klicken Sie auf „Lua“.
    Kopieren Sie den generierten Code und führen Sie ihn in einem Lua-Interpreter aus. Wenn die Ausgabe mit „OK“ endet, ist der Test bestanden.
  7. Klicke auf „Dart“.
    Kopieren Sie den generierten Code und führen Sie ihn in einem Dart-Interpreter aus. Wenn die Ausgabe mit „OK“ endet, ist der Test bestanden.

Blockgenerator-Tests bearbeiten

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