Testy jednostkowe

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.

Wyróżniamy 2 zestawy testów jednostkowych: testy JS i testy generatora bloków.

Testy JS

Testy JS sprawdzają działanie wewnętrznych funkcji JavaScriptu w rdzeniu Blockly. Testy jednostkowe przeprowadzamy za pomocą narzędzia Mocha, Sinon do wycinka zależności, oraz Chai, aby aserować kod.

Testy przeprowadzane

Zarówno w blockly, jak i w blockly-samples polecenie npm run test uruchamia testy jednostkowe. W Blockly zostaną też uruchomione inne testy, takie jak linting i kompilacja. Możesz też otworzyć tests/mocha/index.html w przeglądarce, aby interaktywnie uruchomić wszystkie testy mocha.

Testy pisania

Testy przeprowadzamy za pomocą interfejsu Mocha TDD. 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 apartament może mieć setup i teardown która będzie wywoływana odpowiednio przed i po każdym teście w danym .

Pomocnicze narzędzia testowe

W przypadku Blockly jest dostępnych wiele funkcji pomocniczych, które mogą okazać się przydatne, z pisania testów. Znajdziesz je w blokach podstawowych i w przykładach w Blockly.

Funkcje pomocnicze obejmują sharedTestSetup i sharedTestTeardown, które są wymagane przed wykonaniem testu i po nim (patrz 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 bloków zdefiniowanych w elementach defineBlocksWithJsonArray.
  • Deklaruje kilka właściwości kontekstu this, które mają być dostępne:
    • this.clock (nie należy go jednak przywracać, ponieważ może to spowodować problemy w sharedTestTeardown)
    • this.eventsFireStub
    • this.sharedCleanup (do użycia z addMessageToCleanupaddBlockTypeToCleanup) (UWAGA: jeśli blok został zdefiniowany za pomocą elementu defineBlocksWithJsonArray, nie musisz używać elementu addBlockTypeToCleanup).

Funkcja ma 1 opcjonalny parametr options, który służy do konfigurowania. Obecnie jest używane tylko do określenia, czy należy zawrzeć fragment Blockly.Events.fire, który ma zostać uruchomiony natychmiast (domyślnie jest to skrócony).

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”).
  • Przywraca wszystkie wycinki.
  • Czyści wszystkie typy bloków dodanych za pomocą defineBlocksWithJsonArray i addBlockTypeToCleanup.
  • Czyści wszystkie wiadomości dodane przez addMessageToCleanup.

Wymagania testu

  • Każdy test musi wywoływać metodę sharedTestSetup.call(this); jako pierwszy wiersz w funkcji konfiguracji pakietu zewnętrznego i sharedTestTeardown.call(this); jako ostatniego wiersza dezaprobaty pakietu dla pliku.
  • Jeśli potrzebujesz workspace z ogólnym zestawem narzędzi, możesz użyć jednego z wstępnie skonfigurowanych zestawów narzędzi w teście index.html. Przykład znajdziesz poniżej.
  • Urządzenie this.workspace należy wyrzucić w odpowiedni sposób. W większości testów określić funkcję this.workspace w najbardziej zewnętrznym pakiecie i używać jej we wszystkich kolejnych testowania, ale w niektórych przypadkach można je zdefiniować lub przeformułować, (na przykład jeden z testów wymaga obszaru roboczego z innymi opcjami niż w pierwotnej konfiguracji). Po zakończeniu testu należy go wyrzucić.
    • Jeśli zdefiniujesz funkcję this.workspace w najbardziej zewnętrznym pakiecie i nigdy nie zmienisz definicji nie musisz nic więcej robić. Zostanie on automatycznie usunięty przez sharedTestTeardown.
    • Jeśli określisz this.workspace po raz pierwszy w apartamencie wewnętrznym (tj. nie została ona zdefiniowana w najbardziej zewnętrznym pakiecie), musisz ręcznie aby je utylizować, wywołując workspaceTeardown.call(this, this.workspace) w czasie demontażu tej aranżacji.
    • Jeśli zdefiniujesz this.workpace w zewnętrznym zbiorze testów, ale potem zdefiniujesz go ponownie w wewnętrznym zbiorze testów, musisz najpierw wywołać workspaceTeardown.call(this, this.workspace) przed ponownym zdefiniowaniem, aby usunąć oryginalną przestrzeń roboczą zdefiniowaną w zbiorze najwyższego poziomu. Ty musi też ręcznie usunąć nową wartość, wywołując workspaceTeardown.call(this, this.workspace) ponownie w komponencie apartament wewnętrzny.

Struktura testu

Testy jednostkowe zwykle mają ustaloną strukturę, którą można podsumować jako uporządkuj, wykonaj, sprawdź.

  1. Rozmieść: określ stan świata i wszelkie niezbędne warunki do badanego zachowania.
  2. Działanie: wywołaj testowany kod, aby aktywować testowane działanie.
  3. Zakładanie: twierdzenie dotyczące wartości zwracanej lub interakcji z udawanymi obiektami w celu sprawdzenia poprawności.

W prostym teście może nie być żadnego zachowania do zorganizowania, a działanie i Etapy żądania można łączyć przez wbudowanie wywołania do testowanego kodu w funkcji asercji. W bardziej złożonych przypadkach testy będą bardziej czytelne do 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');
    });
  });
});

Co warto wiedzieć z tego przykładu:

  • W apartamencie mogą znajdować się inne apartamenty z dodatkowymi obiektami setup i teardown .
  • Każdy zestaw i każdy test ma nazwę opisową.
  • Asercje czai są używane do formułowania asercji dotyczących kodu.
    • Możesz podać opcjonalny argument ciągu znaków, który będzie wyświetlany, jeśli test się nie powiedzie. Ułatwia to debugowanie niedziałających testów.
    • Kolejność parametrów to chai.assert.equal(actualValue, expectedValue, optionalMessage). Jeśli zamienisz actual i expected: komunikaty o błędach nie będą miały sensu.
  • Sinon służy do tworzenia metod typu stub, gdy nie chcesz wywoływać rzeczywistego kodu. W tym przykładzie nie chcemy wywoływać funkcji prawdziwych danych, ponieważ nie jest ona istotna dla tego testu. Interesuje nas tylko sposób wykorzystania wyników przez testowaną metodę. Sinon łączy funkcję getMetrics, aby zwrócić szablon odpowiedzi, który możemy łatwo sprawdzić w asercjach testowych.
  • Metody setup dla każdego pakietu powinny zawierać tylko ogólną konfigurację, która dotyczy wszystkich testów. Jeśli test określonego zachowania opiera się na określony warunek musi być wyraźnie określony w odpowiednich test.

Testy debugowania

  • Możesz otworzyć testy w przeglądarce i użyć narzędzi dla programistów, aby ustawić punkty kontrolne i sprawdzić, czy testy niespodziewanie się nie powielają (lub niespodziewanie przechodzą).
  • Ustaw .only() lub .skip() w teście lub pakiecie, aby uruchomić tylko ten zestaw testów. lub pomiń 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 generatorów blokowych

Każdy blok ma własne testy jednostkowe. Testy te potwierdzają, że bloki generują niż funkcje zgodne z przeznaczeniem.

  1. Otwórz tests/generators/index.html w Firefoksie lub Safari. Pamiętaj, że Chrome i Opera mają ograniczenia bezpieczeństwa, które uniemożliwiają ładowanie testów z lokalnego pliku „file://” system (problemy 41024) i 47416).
  2. W menu kliknij odpowiednią część systemu, którą chcesz przetestować, a następnie kliknij „Wczytaj”. Blokady powinny pojawić się w obszarze roboczym.
  3. Kliknij „JavaScript”.
    Skopiuj i uruchom wygenerowany kod w konsoli JavaScriptu. Jeśli dane wyjściowe zakończą się „OK”, oznacza to, że test został zaliczony.
  4. Kliknij „Python”.
    Skopiuj i uruchom wygenerowany kod w interpreterze Pythona. Jeśli dane wyjściowe kończą się na „OK”, test został zaliczony.
  5. Kliknij „PHP”.
    Skopiuj wygenerowany kod i uruchom go w interpreterze PHP. Jeśli w wynikach pojawi się „OK”, test zakończył się powodzeniem.
  6. Kliknij „Lua”.
    Skopiuj i uruchom wygenerowany kod w interpreterze Lua. Jeśli dane wyjściowe kończą się na „OK”, test został zaliczony.
  7. Kliknij „Dart”.
    Skopiuj i uruchom wygenerowany kod w interpreterze Dart. Jeśli w wynikach pojawi się „OK”, test zakończył się powodzeniem.

Edytowanie testów generatora bloków

  1. Wczytaj tests/generators/index.html w przeglądarce.
  2. Wybierz z menu odpowiednią część systemu i kliknij „Wczytaj”. Blokady powinny pojawić się w obszarze roboczym.
  3. Wprowadź zmiany lub uzupełnij bloki.
  4. Kliknij „XML”.
  5. Skopiuj wygenerowany plik XML do odpowiedniego pliku w programie tests/generators/.