Po zmianie lub dodaniu kodu należy uruchomić istniejące testy jednostkowe oraz rozważyć napisanie kolejnych. Wszystkie testy są wykonywane na nieskompresowanych wersjach kodu.
Istnieją 2 zbiory testów jednostkowych: testy JS i testy generatora bloków.
Testy JS
Testy JS sprawdzają działanie wewnętrznych funkcji JavaScriptu w rdzeniu Blockly. Korzystamy z mechanizmu Mocha do testów jednostkowych, Sinon do ograniczania zależności i Chai do tworzenia asercji kodu.
Uruchamianie testów
Zarówno w blockly, jak i w blockly-samples polecenie npm run test
uruchamia testy jednostkowe. W blokowanych momentach zostaną też uruchomione inne testy, takie jak lintowanie i kompilacja. Możesz też otworzyć tests/mocha/index.html
w przeglądarce, aby interaktywnie uruchomić wszystkie testy Mocha.
Testy pisania
Do testowania używamy interfejsu TDD Mocha. Testy są grupowane w pakiety, które mogą zawierać dodatkowe podpakiety lub testy. Zazwyczaj każdy komponent Blockly (np. toolbox
lub workspace
) ma własny plik testowy, który zawiera co najmniej 1 test. Każdy pakiet może mieć metodę setup
i teardown
, która będzie wywoływana odpowiednio przed każdym testem w danym pakiecie i po nim.
Pomocnicze narzędzia testowe
Mamy kilka funkcji pomocniczych dostępnych w Blockly, które mogą być przydatne podczas pisania testów. Można je znaleźć w rdzeniu i w blockly-samples.
Funkcje pomocnicze to sharedTestSetup
i sharedTestTeardown
, które należy wywołać przed i po testach (patrz sekcja Wymagania).
sharedTestSetup
:
- Konfiguruje fałszywe minutniki Sinon (w niektórych testach musisz użyć
this.clock.runAll
). - Stubs Blockly.Events.fire uruchamia się natychmiast (można skonfigurować).
- Konfiguruje automatyczne czyszczenie typów blokowania zdefiniowanych w elementach
defineBlocksWithJsonArray
. - Deklaruje kilka właściwości w kontekście
this
, które mają być dostępne:this.clock
(nie należy go jednak przywracać, ponieważ może to spowodować problemy wsharedTestTeardown
)this.eventsFireStub
this.sharedCleanup
(do użycia zaddMessageToCleanup
iaddBlockTypeToCleanup
) (UWAGA: jeśli blok został zdefiniowany za pomocą elementudefineBlocksWithJsonArray
, nie musisz używać elementuaddBlockTypeToCleanup
).
Funkcja ma 1 opcjonalny parametr options
, który służy do konfigurowania. Obecnie służy tylko do określenia, czy stub Blockly.Events.fire
ma zostać wywołany natychmiast (domyślnie tak się stanie).
sharedTestTeardown
:
- Usuwa obszar roboczy
this.workspace
(w zależności od miejsca zdefiniowania, więcej informacji znajdziesz w sekcji Wymagania dotyczące testów). - przywraca wszystkie puste miejsca;
- Usuwa wszystkie typy bloków dodane za pomocą
defineBlocksWithJsonArray
iaddBlockTypeToCleanup
. - Czyści wszystkie wiadomości dodane przez
addMessageToCleanup
.
Wymagania dotyczące testów
- Każdy test musi wywoływać
sharedTestSetup.call(this);
jako pierwszy wiersz konfiguracji pakietu zewnętrznego isharedTestTeardown.call(this);
jako ostatni wiersz dezaktywacji najbardziej zewnętrznego pakietu pliku. - Jeśli potrzebujesz obszaru roboczego z ogólnym zestawem narzędzi, możesz użyć jednego z gotowych zestawów narzędzi do testu
index.html
. Przykład znajdziesz poniżej. - Urządzenie
this.workspace
należy wyrzucić w odpowiedni sposób. W większości testów definiujesz zmiennąthis.workspace
w zewnętrznym pakiecie i używasz jej we wszystkich kolejnych testach, ale w niektórych przypadkach możesz ją zdefiniować lub zdefiniować ponownie w wewnętrznym pakiecie (np. gdy jeden z testów wymaga workspace z innymi opcjami niż te, które zostały pierwotnie skonfigurowane). Po zakończeniu testu należy go wyrzucić.- Jeśli zdefiniujesz element
this.workspace
w zewnętrznym zestawie i nigdy go nie zmodyfikujesz, nie musisz podejmować żadnych dodatkowych działań. Zostanie on automatycznie usunięty przez firmęsharedTestTeardown
. - Jeśli
this.workspace
zostanie zdefiniowany po raz pierwszy w podzestawie wewnętrznym (czyli nie został zdefiniowany w zewnętrznym podzestawie), musisz go usunąć ręcznie, wywołując funkcjęworkspaceTeardown.call(this, this.workspace)
w ramach rozkładania tego podzestawu. - Jeśli zdefiniujesz usługę
this.workpace
w zewnętrznym pakiecie, a potem zdefiniujesz ją ponownie w wewnętrznym pakiecie testowym, musisz najpierw wywołać metodęworkspaceTeardown.call(this, this.workspace)
przed ponownym zdefiniowaniem jej w celu dezaktywacji oryginalnego obszaru roboczego zdefiniowanego w pakiecie najwyższego poziomu. Musisz też ręcznie usunąć nową wartość, ponownie wywołując funkcjęworkspaceTeardown.call(this, this.workspace)
podczas rozkładania tego pakietu wewnętrznego.
- Jeśli zdefiniujesz element
Struktura testu
Testy jednostkowe zwykle mają ustaloną strukturę, którą można podsumować jako uporządkuj, wykonaj, sprawdź.
- Uporządkuj: ustaw stan świata i wszystkie warunki wymagane do testowanego zachowania.
- Działanie: wywołaj testowany kod, aby aktywować testowane działanie.
- Assert twierdzenie dotyczące wartości zwracanej lub interakcji z udawanymi obiektami w celu sprawdzenia poprawności.
W przypadku prostego testu zachowanie może nie wymagać aranżacji, a etapy działania i sprawdzenia można połączyć, umieszczając w zasadzie wywołanie kodu poddanego testom. W przypadku bardziej skomplikowanych przypadków testy będą łatwiejsze do odczytania, jeśli będziesz przestrzegać tych 3 etapów.
Oto przykładowy plik testowy (uproszczony w rzeczywistości).
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');
});
});
});
Uwagi dotyczące tego przykładu:
- W ramach pakietu mogą się znajdować inne pakiety, które mają dodatkowe metody
setup
iteardown
. - Każdy zestaw i każdy test ma nazwę opisową.
- Założenia Chai służą do tworzenia założeń dotyczących kodu.
- Możesz podać opcjonalny argument w postaci ciągu, który będzie wyświetlany w przypadku niepowodzenia testu. Ułatwia to debugowanie niedziałających testów.
- Kolejność parametrów to
chai.assert.equal(actualValue, expectedValue, optionalMessage)
. Jeśli zamieniszactual
naexpected
, komunikaty o błędach nie będą miały sensu.
- Metoda Sinon jest stosowana, gdy nie chcesz wywoływać prawdziwego kodu. W tym przykładzie nie chcemy wywoływać funkcji realMetrics, ponieważ nie jest ona istotna dla tego testu. Interesuje nas tylko sposób wykorzystania wyników przez testowaną metodę. Sinon zastępuje funkcję
getMetrics
, aby zwracała gotową odpowiedź, którą możemy łatwo sprawdzić w naszych stwierdzeniach testowych. - Metody
setup
dla każdego pakietu powinny zawierać tylko ogólną konfigurację, która ma zastosowanie do wszystkich testów. Jeśli testowanie określonego zachowania zależy od określonego warunku, należy wyraźnie podać ten warunek w odpowiednim teście.
Testy debugowania
- Testy możesz otworzyć w przeglądarce i użyć narzędzi dla programistów, aby ustawić punkty przerwania i sprawdzić, czy testy nie wypadły nieoczekiwanie (lub okazały się nieoczekiwane).
Ustaw
.only()
lub.skip()
dla testu lub zestawu testów, aby uruchomić tylko ten zestaw testów lub pominąć test. 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.
Testy generatora bloków
Każdy blok ma własne testy jednostkowe. Te testy sprawdzają, czy bloki generują kod, który działa zgodnie z oczekiwaniami.
- Otwórz
tests/generators/index.html
w Firefoksie lub Safari. Pamiętaj, że Chrome i Opera mają ograniczenia bezpieczeństwa, które uniemożliwiają wczytywanie testów z lokalnego systemu „file://” (problemy 41024 i 47416). - W menu wybierz odpowiednią część systemu, którą chcesz przetestować, a następnie kliknij „Wczytaj”. W obszarze roboczym powinny pojawić się bloki.
- Kliknij „JavaScript”.
Skopiuj wygenerowany kod i uruchom go w konsoli JavaScriptu. Jeśli dane wyjściowe kończą się komunikatem „OK”, test został zaliczony. - Kliknij „Python”.
Skopiuj i uruchom wygenerowany kod w interpreterze Pythona. Jeśli dane wyjściowe kończą się na „OK”, test został zaliczony. - Kliknij „PHP”.
Skopiuj wygenerowany kod i uruchom go w interpreterze PHP. Jeśli dane wyjściowe kończą się na „OK”, test został zaliczony. - Kliknij „Lua”.
Skopiuj i uruchom wygenerowany kod w interpreterze Lua. Jeśli w wyniku pojawi się „OK”, test zakończył się powodzeniem. - Kliknij „Dart”.
Skopiuj wygenerowany kod i uruchom go w tłumaczu Dart. Jeśli dane wyjściowe kończą się na „OK”, test został zaliczony.
Edytowanie testów generatora bloków
- Wczytaj
tests/generators/index.html
w przeglądarce. - W menu wybierz odpowiednią część systemu, a potem kliknij „Wczytaj”. W obszarze roboczym powinny pojawić się bloki.
- wprowadzać żadnych zmian ani dodatków w blokach.
- Kliknij „XML”.
- Skopiuj wygenerowany plik XML do odpowiedniego pliku w folderze
tests/generators/
.