Po zmianie lub dodaniu kodu uruchom istniejące testy jednostkowe i zastanów się nad napisaniem kolejnych. Wszystkie testy są wykonywane na nieskompresowanych wersjach kodu.
Dostępne są 2 zestawy testów jednostkowych: testy JS i testy z generatorem bloków.
Testy JS
Testy JS potwierdzają działanie wewnętrznych funkcji JavaScript w rdzeni Blockly. Używamy Mocha do testów jednostkowych, Sinon do skrócenia zależności oraz Chai do twierdzeń o kodzie.
Przeprowadzane testy
Zarówno w przypadku prób blokowych, jak i blokowanych, npm run test
uruchomi testy jednostkowe. W bloku uruchomione zostaną też inne testy, takie jak lintowanie i kompilacja. Możesz też otworzyć tests/mocha/index.html
w przeglądarce, aby interaktywnie przeprowadzać wszystkie testy mokki.
Pisanie testów
Do testów używamy interfejsu Mocha TDD. Testy są podzielone na pakiety, które mogą zawierać zarówno dodatkowe podpakiety, jak i testy. Zasadniczo każdy komponent Blockly (np. toolbox
czy workspace
) ma własny plik testowy, który zawiera co najmniej 1 pakiet. Każdy pakiet może zawierać metody setup
i teardown
, które będą wywoływane odpowiednio przed każdym testem w danym pakiecie i po nim.
Pomocnicy w testowaniu
Dla Blockly mamy wiele funkcji pomocniczych, które mogą być przydatne podczas pisania testów. Można je znaleźć w podstawowych i fragmentach blokowych.
Do funkcji pomocniczych należą sharedTestSetup
i sharedTestTeardown
, które są wymagane, aby trzeba je było wywoływać przed testami i po nich (patrz sekcja Wymagania).
sharedTestSetup
:
- Konfiguruje fałszywe liczniki czasu (w niektórych testach trzeba użyć
this.clock.runAll
). - Zdarzenie Stubs Blockly.Events.fire będzie uruchamiać się natychmiast (można skonfigurować).
- Konfiguruje automatyczne czyszczenie typów blockType zdefiniowanych przy użyciu
defineBlocksWithJsonArray
. - Deklaruje w kontekście
this
kilka właściwości, które mają być dostępne:this.clock
(ale nie należy go przywrócić, ponieważ spowoduje to problemy w zadaniusharedTestTeardown
)this.eventsFireStub
this.sharedCleanup
(do użytku zaddMessageToCleanup
iaddBlockTypeToCleanup
) (UWAGA: nie musisz używaćaddBlockTypeToCleanup
, jeśli blok został zdefiniowany za pomocądefineBlocksWithJsonArray
)
Funkcja ma 1 opcjonalny parametr options
do skonfigurowania konfiguracji. Obecnie służy ona tylko do określenia, czy kod Blockly.Events.fire
powinien zostać natychmiast uruchomiony (domyślnie jest to niegotowe).
sharedTestTeardown
:
- Usuwa obszar roboczy
this.workspace
(w zależności od tego, gdzie został zdefiniowany, więcej informacji znajdziesz w sekcji Wymagania dotyczące testów). - Przywróć wszystkie namiastki.
- Usuwa wszystkie typy blokad dodane przez
defineBlocksWithJsonArray
iaddBlockTypeToCleanup
. - Czyści wszystkie wiadomości dodane przy użyciu filtra
addMessageToCleanup
.
Wymagania testowe
- Każdy test musi wywołać
sharedTestSetup.call(this);
jako pierwszy wiersz konfiguracji pakietu najbardziej zewnętrznego isharedTestTeardown.call(this);
jako ostatni wiersz podczas dezaktywacji pakietu najbardziej zewnętrznego pliku. - Jeśli potrzebujesz obszaru roboczego ze standardowym zestawem narzędzi, możesz użyć jednego z gotowych zestawów narzędzi na potrzeby testów
index.html
. Przykład znajdziesz poniżej. this.workspace
należy pozbyć się poprawnie. W większości testów definiujeszthis.workspace
w pakiecie zewnętrznym i używasz go we wszystkich kolejnych testach, ale w niektórych przypadkach możesz zdefiniować ją lub zmienić na nowo w pakiecie wewnętrznym (na przykład jeden z testów wymaga obszaru roboczego z innymi opcjami niż pierwotnie skonfigurowane). Należy zutylizować się po zakończeniu testu.- Jeśli zdefiniujesz właściwość
this.workspace
w najbardziej zewnętrznym pakiecie i nigdy jej nie zmienisz, nie musisz nic robić. Zostanie automatycznie usunięta przezsharedTestTeardown
. - Jeśli po raz pierwszy zdefiniujesz właściwość
this.workspace
w pakiecie wewnętrznym (czyli nie została zdefiniowana w najbardziej zewnętrznym), musisz ją pozbyć się ręcznie, wywołującworkspaceTeardown.call(this, this.workspace)
przy demontażu pakietu. - Jeśli zdefiniujesz obiekt
this.workpace
w najbardziej zewnętrznym pakiecie, a następnie zdefiniujesz go ponownie w wewnętrznym pakiecie testowym, musisz najpierw wywołać metodęworkspaceTeardown.call(this, this.workspace)
przed ponownym zdefiniowaniem jej w celu usunięcia oryginalnego obszaru roboczego zdefiniowanego w pakiecie najwyższego poziomu. Nową wartość musisz też usunąć ręcznie, ponownie wywołując metodęworkspaceTeardown.call(this, this.workspace)
podczas wyłączania tego pakietu wewnętrznego.
- Jeśli zdefiniujesz właściwość
Struktura testów
Testy jednostkowe mają zwykle strukturę opartą na zbiorze danych, który można podsumować w postaci rozmieszczania, wykonywania i wykonywania.
- Rozmieść: określ stan świata i wszelkie warunki wymagane do przeprowadzenia testu.
- Działanie: wywołaj testowany kod, aby aktywować testowane zachowanie.
- Zgłoś: zgłoś wartość zwracaną lub interakcje z imitowanymi obiektami, aby zweryfikować ich poprawność.
W prostym teście może nie być żadnych działań do ustawienia, a etapy działania i zgłaszania można połączyć, umieszczając wywołanie do testowanego kodu w twierdzeniu. W bardziej złożonych przypadkach testy będą bardziej czytelne, jeśli będziesz przestrzegać tych 3 etapów.
Oto przykładowy plik testowy (uproszczony w stosunku do rzeczywistego).
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');
});
});
});
Rzeczy, o których warto pamiętać w tym przykładzie:
- Pakiet może zawierać inne pakiety zawierające dodatkowe metody
setup
iteardown
. - Każdy pakiet i test ma opisową nazwę.
- Asercje czai służą do wyrażania potwierdzeń dotyczących kodu.
- Możesz podać opcjonalny argument tekstowy, który będzie wyświetlany w razie niepowodzenia testu. Ułatwia to debugowanie nieudanych testów.
- Kolejność parametrów:
chai.assert.equal(actualValue, expectedValue, optionalMessage)
. Jeśli zamieniszactual
iexpected
, komunikaty o błędach nie będą miały sensu.
- Sinon służy do pomijania metod, gdy nie chcesz wywoływać prawdziwego kodu. W tym przykładzie nie chcemy wywoływać funkcji danych rzeczywistych, ponieważ nie ma ona związku z testem. Interesuje nas tylko, jak testowana metoda
wykorzystuje wyniki. Sinon przerywa działanie funkcji
getMetrics
, aby zwrócić szablon odpowiedzi, którą możemy łatwo sprawdzić w potwierdzeniach testowych. - Metody
setup
poszczególnych pakietów powinny zawierać tylko konfigurację ogólną obowiązującą w przypadku wszystkich testów. Jeśli test na konkretne zachowanie zależy od określonego warunku, należy go wyraźnie wskazać w odpowiednim teście.
Testy debugowania
- Możesz otworzyć testy w przeglądarce i użyć narzędzi dla programistów, aby ustawić punkty przerwania i sprawdzić, czy testy kończą się nieoczekiwaniem (lub kończą się przypadkowo).
Ustaw
.only()
lub.skip()
w teście lub pakiecie, aby uruchomić tylko ten zestaw testów albo pomiń jeden z nich. Na przykład:suite.only('Workspace', function () { suite('updateToolbox', function () { test('test name', function () { // ... }); test.skip('test I don’t care about', function () { // ... }); }); });
Pamiętaj, aby usunąć je przed zatwierdzeniem kodu.
Blokuj testy generatorów
Każdy blok ma własne testy jednostkowe. Te testy sprawdzają, czy bloki generują kod niż funkcje.
- Wczytaj
tests/generators/index.html
w Firefoksie lub Safari. W Chrome i Operze obowiązują ograniczenia zabezpieczeń, które uniemożliwiają ładowanie testów z lokalnego systemu „file://” (problemy 41024 i 47416). - Wybierz w menu odpowiednią część systemu do przetestowania i kliknij „Wczytaj”. W obszarze roboczym powinny pojawić się blokady.
- Kliknij „JavaScript”.
Skopiuj i uruchom wygenerowany kod w konsoli JavaScriptu. Jeśli wynik kończy się na „OK”, test się zakończył. - Kliknij „Python”.
Skopiuj i uruchom wygenerowany kod w interpreterze Pythona. Jeśli dane wyjściowe zakończą się na „OK”, test się nie powiódł. - Kliknij „PHP”.
Skopiuj i uruchom wygenerowany kod w interpreterze języka PHP. Jeśli dane wyjściowe zakończą się na „OK”, test się nie powiódł. - Kliknij „Lua”.
Skopiuj i uruchom wygenerowany kod w tłumaczeniu Lua. Jeśli dane wyjściowe zakończą się na „OK”, test się nie powiódł. - Kliknij „Dart”.
Skopiuj i uruchom wygenerowany kod w interpreterze Dart. Jeśli dane wyjściowe zakończą się na „OK”, test się nie powiódł.
Edytowanie testów generatorów bloków
- Wczytaj
tests/generators/index.html
w przeglądarce. - Z menu wybierz odpowiednią część systemu i kliknij „Wczytaj”. W obszarze roboczym powinny pojawić się blokady.
- Wprowadź w blokach dowolne zmiany lub dodatki.
- Kliknij „XML”.
- Skopiuj wygenerowany kod XML do odpowiedniego pliku w usłudze
tests/generators/
.