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 insharedTestTeardown
auftreten)this.eventsFireStub
this.sharedCleanup
(zur Verwendung mitaddMessageToCleanup
undaddBlockTypeToCleanup
) (HINWEIS: Sie müssenaddBlockTypeToCleanup
nicht verwenden, wenn Sie den Block mitdefineBlocksWithJsonArray
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
undaddBlockTypeToCleanup
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 undsharedTestTeardown.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 Siethis.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 vonsharedTestTeardown
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 SieworkspaceTeardown.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 zuerstworkspaceTeardown.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 dazuworkspaceTeardown.call(this, this.workspace)
im Teardown dieser inneren Suite noch einmal auf.
- Wenn Sie
Teststruktur
Einheitentests folgen im Allgemeinen einer festgelegten Struktur, die als arrange, action, assert. zusammengefasst werden kann.
- Anordnen: Legen Sie den Zustand der Welt und alle erforderlichen Bedingungen für das zu testende Verhalten fest.
- Handeln: Rufen Sie den zu testenden Code auf, um das zu testende Verhalten auszulösen.
- 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
- undteardown
-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 Sieactual
undexpected
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.
- 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). - 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.
- 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. - 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. - 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. - 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. - 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
- Laden Sie
tests/generators/index.html
in einem Browser. - 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.
- Nehmen Sie die gewünschten Änderungen oder Ergänzungen vor.
- Klicken Sie auf „XML“.
- Kopieren Sie den generierten XML-Code in die entsprechende Datei in
tests/generators/
.