Bezpieczeństwo.  Organizować coś.  Internet.  Powrót do zdrowia.  Instalacja

Prosta aplikacja JavaScript do testowania wiedzy. Nauka testowania kodu JavaScript

Pewnego dnia mój przyjaciel wyraził swoje zdziwienie, jak JavaScript może zostać użyty do napisania poważnych produktów dla przedsiębiorstw, skoro nie ma on kompilatora. Tak naprawdę decydującą rolę w tworzeniu kodu wysokiej jakości odgrywa nie fakt posiadania kompilatora dla języka programowania, ale prawidłowo dobrany i dobrze dostrojony proces techniczny tworzenia systemu oprogramowania.

Proces ten powinien obejmować zestaw środków kontroli jakości i efektywności pracy programisty. Takimi narzędziami mogą być: testy jednostkowe i integracyjne, integracja ciągła (Continious Integration, CI), zbieranie i analiza różnych metryk (np. bardzo długich metod w nDepend), sprawdzanie zgodności z wymaganiami JsLint, FxCop itp.

W tym artykule chcę Ci powiedzieć jak poprawnie przeprowadzić automatyczne testy jednostkowe i integracyjne Twojego produktu w JavaScript. Tak naprawdę pod tym względem język JavaScript nie różni się zasadniczo od Javy czy C#.

Zwinne, TDD i BDD

Zwykle zaleca się tworzenie zautomatyzowanych testów jednostkowych i integracyjnych dla ukończonych funkcjonalności, aby zmniejszyć ryzyko wystąpienia błędów regresyjnych przy zmianie kodu w przyszłości. W przypadku JavaScript takie testy mogą znacznie uprościć testowanie funkcjonalności systemu w różnych przeglądarkach poprzez automatyzację kroków zapewniających takie testowanie. Ponadto dobrym pomysłem może być napisanie testu jednostkowego lub integracyjnego dla każdego zamkniętego błędu w produkcie.

Istnieją również techniki programowania, które wymagają rozpoczęcia logiki kodowania od napisania testu jednostkowego: rozwój oparty na testach (TDD) i rozwój oparty na zachowaniu (BDD). Są one często wykorzystywane w procesie Agile. Przyjrzyjmy się bliżej ich funkcjom.

Rozwój oparty na testach

Rozwój oparty na testach to iteracyjny proces pisania kodu, który powtarza następujące cztery kroki:

Krok 1. Zanim dodasz nowy element logiki, utwórz test jednostkowy, aby przetestować tę logikę;

Krok 2. Uruchom test i upewnij się, że tak jest Nie Karnety;

Krok 3. Napisz najprostszy kod, który sprawi, że test zakończy się sukcesem;

Krok 4. Edytuj kod, aby spełnić wymagania jakościowe, usuń zduplikowany kod i upewnij się, że test przebiegł pomyślnie.

Test jednostkowy to kod, który testuje działanie określonego komponentu (modułu) w izolowanym środowisku. Test integracji odnosi się do kodu, który testuje współpracę wielu komponentów. Aby przetestować moduł w izolowanym środowisku, gdy jest on zależny od innych modułów, stosuje się testy podwójne.

Testuj dwójkę

Podział obiektów pomocniczych stosowanych w testach jednostkowych na kategorie zaczerpnięty został z książki xUnit Test Patterns autorstwa Gerarda Meszarosa. Kategorie te nazywane są łącznie „deblami testowymi”. Istnieją następujące typy dublerów:

  • Podróbka;
  • Atrapa.

Końcówka wartości wyjściowe dla których są one określone z góry. Służy do symulacji interfejsu komponentu zależnego.

Mock jest obiektem pomocniczym zachowanie co jest określone z góry. Służy do symulacji interfejsu zależnego komponentu i sprawdzenia, czy jest on poprawnie używany.

Spy to obiekt pomocniczy do sprawdzania wywoływanych metod i parametrów przekazywanych im w trakcie testu.

Fake to obiekt pomocniczy, który implementuje interfejs komponentu zależnego w uproszczonej formie. Na przykład do celów testów jednostkowych można mieć bazę danych w pamięci zamiast relacyjnej bazy danych używanej w produkcyjnej wersji produktu.

Manekin to obiekt pomocniczy, do którego odniesienie lub przekazanie jest wymagane przez sygnaturę metody lub jakikolwiek inny kontrakt, ale którego rzeczywista wartość nigdy nie jest używana.

Różnica między Stubem a Mockiem polega na sposobie weryfikacji wyników testu. W przypadku Stuba stan obiektu sprawdzany jest na koniec testu. W przypadku Mocka badanie sprawdza, czy przedmiot jest używany dokładnie tak, jak opisano podczas rejestracji. Szczegóły można znaleźć w notatce Mocks Aren't Stubs autorstwa Martina Fowlera, a tutaj podam tylko przykład.

Końcówka Kpina
„połączenie testowe powinno rozpocząć odpytywanie”: funkcja () ( this.client.url = "/my/url"; sinon.stub(ajax, "poll").returns()); this.client.connect(); sinon .assert. CallWith(ajax.poll, "/my/url"); ) „połączenie testowe powinno rozpocząć odpytywanie”: funkcja () ( this.client.url = "/my/url"; var mock = sinon.mock(ajax) mock.expects("poll") .withArgs("/my/url ").returns(); this.client.connect(); mock.verify(); )
Rozwój oparty na zachowaniu

Iteracyjne podejście do tworzenia oprogramowania poprzez implementację wymagań funkcjonalnych to znany już styl programowania zorientowany na wyniki i oparty na testach. W procesie BDD wykonywane są sekwencyjnie trzy następujące kroki:

Krok 1. Określenie wymagań funkcjonalnych dla wdrażanego modułu w formie testów;

Krok 2. Kodowanie modułu;

Krok 3. Weryfikacja spełnienia wszystkich życzeń klienta lub analityka biznesowego () poprzez sprawdzenie wyników przeprowadzanych testów.

Podczas pisania testów w stylu BDD bardzo wygodne jest korzystanie z obiektów Mock, ponieważ doskonale odzwierciedlają one wymagania funkcjonalne stawiane komponentowi. Tym samym testy w procesie BDD mogą służyć jako sformalizowana reprezentacja problemu (historia użytkownika) w ujęciu Scrumowym, co pozwala zaoszczędzić czas na pisaniu specyfikacji technicznych i dokumentacji gotowego produktu.

Jaki powinien być framework do testów jednostkowych JavaScript?

Kompletne narzędzie JavaScript do testów jednostkowych i integracyjnych powinno składać się z następujących elementów:

  • Biblioteka asercji (zestaw metod sprawdzania stanu komponentu na koniec każdego testu);
  • Biblioteka Mock (narzędzie do generowania obiektów Mock i innych „duplikatów”);
  • Test Runner (narzędzie do automatycznego uruchamiania testów z obsługą większości przeglądarek, w tym przeglądarek iOS i Android);
  • Blok przyłączeniowy do popularnych systemów ciągłej integracji.
Strategie testów jednostkowych kodu JavaScript

Obecnie istnieją trzy strategie testowania jednostkowego kodu JavaScript (więcej szczegółów można znaleźć w trzecim rozdziale książki Christiana Johansena: Test-Driven JavaScript Development):

  • Testowanie w przeglądarce;
  • Testowanie bez głowy;
  • Testowanie wzdłuż ścieżki JsTestDriver.

Testowanie w przeglądarce polega na uruchomieniu wszystkich testów jednostkowych i integracyjnych ze strony HTML, którą programista otwiera niezależnie w wybranych przeglądarkach. To podejście jest proste i intuicyjne. Jego wadą jest jednak to, że nie przewiduje możliwości włączenia takich testów do Ciągłej Integracji. Dodatkowo ręczne uruchamianie strony HTML w dziesięciu lub więcej przeglądarkach i ciągłe naciskanie klawisza „F5” może być uciążliwe dla programisty.

Testowanie bezgłowe oznacza, że ​​cały kod JavaScript jest testowany na emulatorze, który można napisać w języku Java, Ruby, JavaScript, C++ itp. Najbardziej znanym obecnie emulatorem jest PhantomJS, który jest silnikiem WebKit uruchamianym z wiersza poleceń. Wśród zalet emulatora można zauważyć, że można go łatwo zastosować w Ciągłej Integracji, a także pozwala na zautomatyzowanie uruchamiania wszystkich testów z linii poleceń. Jednak takie podejście ma istotną wadę - kod nie jest testowany na prawdziwych przeglądarkach, istnieje więc ryzyko pominięcia błędów przeglądarki, które nie są odtwarzane na emulatorze. Przed pojawieniem się JsTestDriver często można było zobaczyć, że testowanie w przeglądarce było łączone z testowaniem bezgłowym, ponieważ doskonale się uzupełniały.

Testowanie kodu to integralny cykl tworzenia oprogramowania. Początkujące zespoły programistyczne często nie doceniają jego roli i sprawdzają funkcjonalność aplikacji w staromodny sposób – „działa, OK”. Wcześniej czy później ta strategia zawiedzie i narzędzie do śledzenia błędów zaczyna być przytłoczone niezliczoną armią zadań. Aby nie wpaść w taką pułapkę, polecam raz na zawsze zrozumieć niuanse testowania kodu JavaScript.

JavaScript nie jest już taki sam

Dzisiaj JavaScript to coś więcej niż tylko język, który urozmaica wygląd aplikacji. Czasy, w których JavaScript był używany do żartów lub tworzenia menu, bezpowrotnie minęły. Jest to teraz samodzielny język, który działa równie dobrze zarówno na kliencie, jak i na serwerze. Znacząco wzrosła rola JavaScriptu, co oznacza, że ​​pisząc kod nie należy wstydzić się stosowania praktyk, które sprawdziły się w innych językach programowania.

Co rozumiem przez praktyki i paradygmaty? Oczywiście wzorzec architektoniczny MVC (kontroler widoku modelu) i wzorce organizacji kodu. Stosując się do tych prostych trików, będziesz w stanie napisać lepszy kod, który będzie nie tylko łatwy w utrzymaniu, ale także będzie miał możliwość automatycznego testowania.

Błąd większości testerów

Nie jest tajemnicą, że najpopularniejszą metodą badania zawsze było banalne badanie wzroku. Jego istota jest aż do wstydu prosta – piszesz kilka tysięcy linii kodu, rozwiązujesz problem i uruchamiasz swoje dzieło. Pobawiłem się, kliknąłem, wszystko wydawało się działać, mogłem wrzucić na serwer produkcyjny. Wszystko jest niezwykle proste i przy należytej uwadze programisty (najlepiej indywidualnego pseudonimu „tester”) można polegać na poprawnym działaniu aplikacji.

W praktyce wszystko dzieje się trochę inaczej. Z reguły nie ma osobnego testera. Sam programista próbuje sprawdzić funkcjonalność programu, wykonując sekwencję działań określoną w specyfikacjach technicznych. Bardziej zaawansowane narzędzia do tworzenia kodu automatyzują tego rodzaju testy integracyjne przy użyciu narzędzi takich jak Selenium.

W ten sposób programista ma możliwość wykrycia tylko najpoważniejszych błędów. Niestety, „głupie” i „niezamierzone” działania użytkowników, a także przebiegłe posunięcia w logice biznesowej w 99% przypadków pozostają za kulisami.

Obecność oddzielnej osoby w osobie testera również rozwiązuje problem częściowo i do pewnego czasu. Nawet jeśli zignorujemy dbałość jego sapera o szczegóły, jakość jego testów będzie dążyć do zera w miarę rozwoju aplikacji. Podam przykład z praktyki.

Któregoś dnia przydzielono mnie do opracowania małego programu. Projekt pod względem funkcjonalności przypominał prosty CRM, który wdrożyłem w możliwie najkrótszym czasie. Po otrzymaniu należnego wynagrodzenia przekazałem wszystkie źródła klientowi i na osiem miesięcy zapomniałem o projekcie. Potem zaczęła się zabawa. Klient postanowił poważnie rozszerzyć funkcjonalność programu i zwrócił się do mnie z prośbą o pomoc. Oczywiście wziąłem to i zacząłem rzeźbić nową funkcję za funkcją. Na początku nie było to trudne, jednak gdy doszło do ogólnej integracji funkcjonalności, w moją stronę rzucił się brzęczący rój błędów. Fragmenty kodu zaczęły ze sobą kolidować i musieliśmy spędzić dużo czasu na rozwiązywaniu konfliktów. „No cóż, dlaczego nie zauważyłeś, że wystąpiły problemy z twoją aplikacją?” – zapytają uważni czytelnicy. Uruchomiłem go, jednak w związku z rozrostem aplikacji zabrakło mi czasu i nerwów, aby masowo przetestować wszystkie funkcjonalności. Ograniczyłem się do testowania tylko poszczególnych funkcji i sowicie za to zapłaciłem. Morał z tej historii jest następujący: „Pomyśl o testowaniu jako o integralnej części rozwoju”.

Testy jednostkowe są jak srebrna kula

Testy jednostkowe to najlepszy sposób na zaoszczędzenie nerwów i zwiększenie gwarancji funkcjonalności poszczególnych części aplikacji. Jeśli nigdy nie spotkałeś się z tym strasznym słowem, to pokrótce wyjaśnię. Testy jednostkowe pozwalają zautomatyzować proces testowania i przetestować każdą funkcję aplikacji.

Po zakończeniu opracowywania nowej funkcji (istnieje możliwość napisania testów przed rozpoczęciem programowania) programista pisze specjalny kod w celu przetestowania swojego kodu. W kodzie testowym musisz symulować różne sytuacje i zwracać wartości. Na przykład napisaliśmy funkcję przycinania spacji (trim). Aby przetestować jego działanie, musimy przygotować kilka testów, które pozwolą nam stwierdzić, że:

  • przekazując ciąg „string” na wyjściu otrzymamy „string”;
  • przesyłając terminy „linia 9”, na wyjściu otrzymamy „linię 9”;
  • Możemy także dodać testowanie innych parametrów wejściowych (na przykład zastąpienie znaku spacji tabulatorem). Generalnie im lepiej przykryjemy kod testami i uwzględnimy ewentualne opcje negatywne, tym większa szansa, że ​​w najbardziej kluczowym momencie zostanie nam trochę włosów na głowie.

    W świecie JS testy są zwykle pisane przy użyciu specjalistycznych frameworków. Mają wszystko, czego potrzeba do opisu testów, a także przynajmniej kilka narzędzi do usystematyzowania raportów z postępu testów.

    Testy!= dodatkowy kod

    Programiści, którzy nie korzystają z testów jednostkowych, lubią argumentować, że testowanie jednostkowe wymaga pisania i utrzymywania dodatkowego kodu. Mówią, że w realnych projektach terminy są często napięte i po prostu nie da się napisać dodatkowego kodu.

    Zgadzam się co do napiętych terminów, ale jestem skłonny polemizować w sprawie dodatkowego kodu. Z jednej strony tak, testy wymagają dodatkowego kodu, a co za tym idzie czasu na jego napisanie. Z drugiej strony kod ten działa jak poduszka powietrzna w samochodzie i na pewno zwróci się w miarę rozwoju aplikacji.

    Kiedy nie masz czasu i dręczy Cię chęć rezygnacji z pisania testów, zastanów się trzy razy. W takim przypadku bardziej odpowiednie może być objęcie testami tylko najbardziej skomplikowanych sekcji kodu, niż całkowite porzucenie testowania. Zawsze myśl perspektywicznie, tak jakby w ciągu miesiąca Twój program mógł urosnąć do niespotykanych dotąd rozmiarów.

    Nie każdy kod jest testowany

    Dlaczego mówię, że przed napisaniem głównego kodu należy pomyśleć o testowaniu? Tak, ponieważ kod, który początkowo miał zostać objęty testami jednostkowymi, jest napisany w nieco innym stylu. Nie każdy kod można przetestować. Kod, który łączy logikę i reprezentacje, a także jest upchnięty w obszarach, których nie można odpowiednio przetestować. Tutaj zawsze radzę przestrzegać kilku prostych zasad:

  • Nie trzeba pisać dużych funkcji. Każda funkcja powinna rozwiązywać jeden problem, a nie 100 500 możliwych sytuacji. Nie ma np. potrzeby wpisywania kodu wysyłającego dane na serwer do funkcji odpowiedzialnej za ich przygotowanie;
  • Funkcja składająca się z więcej niż 10 linii kodu jest najprawdopodobniej złą funkcją;
  • Logika i prezentacja nigdy nie powinny iść w parze;
  • QUnit – klasyk od twórców jQuery

    QUnit jest szczególnie popularny wśród programistów JavaScript. Po pierwsze jest dobrze udokumentowany i łatwy w użyciu, po drugie został stworzony przez autorów jQuery. Biblioteka nadaje się do testowania zarówno kodu JavaScript opartego na jQuery, jak i natywnego.

    Najnowszą wersję QUnit możesz pobrać z oficjalnej strony internetowej - http://qunitjs.com/. Biblioteka jest dostarczana w postaci pojedynczego pliku JS i CSS. Załóżmy, że udało Ci się załadować niezbędne komponenty, a jeśli tak, to czas napisać test próbny. Nie idźmy daleko i spróbujmy przetestować wspomnianą powyżej funkcję trim().

    Aby zademonstrować testy, stworzyłem prosty projekt z następującym konstruktorem:

    Index.html – główny plik, w którym będą wyświetlane wyniki testu; - qunit-1.12.0.js – plik biblioteki qunit; - przykład.js – plik zawierający kod do testów (w naszym przypadku opis funkcji trim()); - test.js – plik z testami; - qunit-1.12.0.css – style do projektowania raportu z testami;

    Zawartość plików Index.html i test.js prezentujemy na Listingach 1 i 2. Nas najbardziej interesuje drugi listing, który zawiera deklarację testowanej funkcji (trim()) oraz kod testowy sprawdzający jej funkcjonalność. Należy pamiętać, że samą funkcję trim() można umieścić w dowolnym miejscu; umieściłem ją na drugim liście, aby zaoszczędzić miejsce w magazynie.

    Przyjrzyjmy się teraz samym testom. Aby sprawdzić funkcjonalność naszego kodu, biblioteka Qunit.js oferuje nam szereg metod:

  • test() – opakowanie do opisu testu;
  • ok() – asercja pozwala sprawdzić prawdziwość pierwszego parametru. W naszym przykładzie przekazuję wywołanie zdefiniowanej przez nas funkcji trim() i porównuję ją z wartością, którą spodziewam się otrzymać. Jeżeli warunek jest spełniony, test zostaje zaliczony;
  • równości() – metoda pozwala sprawdzić równość pierwszego i drugiego parametru. Należy od razu zauważyć, że ta metoda wykonuje słabą kontrolę, dlatego nadaje się tylko do wielkości skalarnych;
  • notEqual() jest przeciwieństwem równości(). Wykonywane, jeśli pierwsza wartość nie jest równa drugiej;
  • strictEqual() jest podobna do równości() z jedną różnicą - wykorzystuje ścisłe sprawdzanie (tzn. sprawdza także typ danych);
  • notStrictEqual() – metoda jest przeciwieństwem strictEqual();
  • deepEqual() – metoda dla asercji rekurencyjnych, stosowana dla prymitywów, tablic, obiektów;
  • notDeepEqual() – metoda jest przeciwieństwem deepEqual();
  • raises() – instrukcja służąca do testowania funkcji wywołania zwrotnego, która zgłasza wyjątek;
  • W drugim zestawieniu jasno pokazałem jak zastosować te metody w praktyce. Jeśli uruchomisz przykładowy test w tej formie, wszystkie testy zakończą się pomyślnie (patrz odpowiedni rysunek). Aby zobaczyć różnicę między testami, które zaszły pomyślnie, a tymi, które się nie powiodły, nieznacznie zmodyfikowałem kod jednego z testów. Celowo dodałem błędny wynik do linii testowej, używając funkcji strictEqual() (patrz odpowiedni rysunek).

    Listing 1. Zawartość pliku indeks.html Testowanie za pomocą QUnit Listing 2. Pliki testowe i funkcja trim() trim(string) ( return (string || "").replace(/^\s+|\s+$/g, ""); ) test("Testuj funkcję trim()", funkcja() ( ok(trim(" test ") == "test", "przytnij zewnętrzne przestrzenie"); ok(trim(" 1 ") == "1", "dużo spacji po bokach"); ok(trim(" 24 ") == "24", "spacje i tabulatory po bokach"); równości(trim(""), "" , "Pusta linia "); strictEqual(trim(" ][aker") ));

    Wygląda na to, że uporaliśmy się z testowaniem prostych funkcji. W każdym razie nie mam nic więcej do dodania. Następnie musisz wziąć prawdziwy kod i spróbować samodzielnie napisać testy. Przyjrzyjmy się innemu często spotykanemu zadaniu programistów JavaScript – testowaniu funkcji asynchronicznych. Aplikacja wypełniona kodem JavaScript w 99% współdziała ze stroną serwera za pomocą Ajaxu. Nie możesz też pozostawić tego kodu niezaznaczonego, ale pisanie testów będzie wyglądać trochę inaczej. Spójrzmy na przykład:

    AsyncTest("myAsyncFunc()", funkcja() ( setTimeout(funkcja() ( ok(myAsyncFunc() == true, "Dane przesłano pomyślnie"); start(); ), 500); ));

    Główna różnica między tym przykładem a poprzednim polega na tym, że zamiast opakowania test() używana jest funkcja asyncTest(), co bezpośrednio stwierdza, że ​​jestem zainteresowany testowaniem testów asynchronicznych. Następnie czas oczekiwania zaczynam od 500 ml. sek. W tym czasie funkcja myAsyncFunc() powinna przesłać dane na serwer testowy i jeśli wszystko będzie dobrze, zwrócić wartość true. I tu następuje najciekawszy moment. Po wywołaniu funkcji asyncTest() wątek wykonawczy zostaje zatrzymany, a po zakończeniu testu należy go uruchomić niezależnie. Aby kontrolować przebieg wykonywania, QUnit posiada metody start() i stop().

    Testowanie funkcji asynchronicznych przy użyciu biblioteki QUnit jest dość proste. Ostatni przykład, któremu chciałbym się przyjrzeć, dotyczy napisania testu, który wykonuje kilka kontroli asynchronicznych. Głównym pytaniem, które pojawia się przy takich zadaniach, jest optymalne miejsce rozpoczęcia przepływu realizacji. Oficjalny dokument sugeruje użycie czegoś takiego:

    AsyncTest("myAsyncFunc()", funkcja() ( spodziewaj się(3); //Tutaj przeprowadzamy trzy kontrole ok(myAsyncFunc(), "Uczynić świat lepszym miejscem 1"); ok(myAsyncFunc(), "Uczynić świat lepszym miejscem 2") ; ok(myAsyncFunc(), "Uczyńmy świat lepszym miejscem 3"); setTimeout(funkcja() ( start(); ), 3000); ));

    Testuj działania niestandardowe

    Powinieneś zawsze pamiętać, że wiele elementów interfejsu jest napisanych w JavaScript. Na przykład użytkownik klika alfonsa i coś powinno się wydarzyć w odpowiedzi na jego kliknięcie. W projektach jest ogromna ilość takiego kodu „interfejsu”, który również wymaga pokrycia testami. Zobaczmy, jak możemy symulować naciśnięcie klawisza użytkownika i napiszmy osobny test dla tej akcji. Wyobraźmy sobie, że mamy pewną funkcję rejestrującą wciśnięte klawisze. Podałem jego kod w trzecim wpisie:

    Listing 3. Funkcja rejestrowania naciśnięć klawiszy KeyLogger(target) ( if (!(ta instancja KeyLoggera)) (zwróć nowy KeyLogger(target); ) this.target = target; this.log = ; var self = this; this.target. off („keydown”).on(„keydown”, funkcja(zdarzenie) ( self.log.push(event.keyCode); )); )

    Spróbujmy teraz przetestować tę funkcję. Przede wszystkim w treści testu musimy emulować wciśnięty klawisz. Najłatwiej to zrobić, korzystając z biblioteki jQuery, która pozwala utworzyć zdarzenie w kilku linijkach kodu (patrz Listing 4).

    Listing 4. Kod testowy dla KeyLoggera test("Test rejestrowania kluczy", funkcja() ( var event, $doc = $(document), klucze = KeyLogger($doc); event = $.Event("keydown"); event .keyCode = 9; $doc.trigger(event); równości(keys.log.length, 1, „Klucz zarejestrowany”); równości(keys.log[ 0 ], 9, „Naciśnięcie klawisza z zarejestrowanym kodem 9”); ) );

    Na samym początku zestawienia testów przygotowuję zdarzenie emulujące naciśnięcie klawisza – „keydown”. Będziemy zainteresowani naciśnięciem klawisza Tab (kod 9). Następnie za pomocą metody wyzwalacz() wywoływam przygotowane zdarzenie, po czym mogę przystąpić do testowania. Najpierw sprawdzamy ogólny obraz - czy klawisz został naciśnięty, a następnie jego kod.

    DOM pod przykrywką testów

    Ponieważ Qunit.js umożliwia testowanie działań użytkownika, pisanie testów dla DOM również nie powinno stanowić problemu. To rzeczywiście prawda, a poniższy przykład potwierdzi moje słowa. Nie będę tego komentował, wystarczy spojrzeć na kod i wszystko stanie się jasne:

    Test("Dodaj nowy element div", funkcja() ( var $fixture = $("#qunit-fixture"); $fixture.append("To jest nowy element div"); równości($("div", $urządzenie) .length, 1, "Nowy div został pomyślnie dodany!"); ));

    Phantom.JS – uruchamianie testów z konsoli

    Pisanie testów przy użyciu biblioteki Qunit.js jest wygodne i proste, jednak prędzej czy później uderzy Cię chęć, aby w jakiś sposób zautomatyzować uruchamianie testów i zbieranie wyników. Przykładowo w tym celu mam w DigitalOcean osobną maszynę wirtualną, którą mogę zarządzać jedynie za pomocą konsoli.

    Projekt phantom.js rozwiązuje ten problem dość elegancko. To nie jest kolejny framework do pisania testów jednostkowych, ale pełnoprawna konsolowa wersja silnika WebKit. Krótko mówiąc, ta aplikacja emuluje przeglądarkę. Za pomocą phantom.js można nie tylko zautomatyzować weryfikację wykonania testu, ale także rozwiązać wiele problemów, które prędzej czy później pojawiają się przed programistą: uzyskanie wyników renderowania strony do pliku (png, jpg), sieci funkcje monitorowania (szybkość ładowania, ogólna wydajność itp.), itp.), emulacja działań użytkownika itp. Polecam poświęcić trochę czasu i zapoznać się z oficjalną dokumentacją tego projektu, na pewno znajdziesz coś dla siebie.

    Phantom.js można skompilować na różne platformy (nix, mac OS X, Windows). Jeśli opracujesz wszystko w systemie Windows, nie będzie żadnych problemów - połącz pliki binarne i śmiało. Drobne problemy z uruchomieniem mogą wystąpić, jeśli masz zainstalowane dwie karty wideo, z których jedna to NVidia. W takim przypadku będziesz musiał skorzystać z hacku opisanego na pasku bocznym.

    Spróbujmy zapoznać się z phantom.js w praktyce. Aby uruchomić testy przygotowane w ostatniej sekcji poprzez phantom.js i przenieść wyniki wykonania do konsoli, potrzebujemy specjalnego skryptu ładującego - run-qunit.js. Otwórz konsolę (pracuję na Windowsie, więc używam cmd) i wpisz polecenie w formacie:

    Phantom.exe

    W moim przypadku polecenie uruchomienia wyglądało następująco:

    E:\soft\phantomjs>phantomjs.exe E:\temp\testjsforx\qunit\run-qunit.js file:///E: /temp/testjsforx/qunit/index.html Wynik jego wykonania: Testy zakończone w 2592 milisekundy. 9 twierdzeń z 9 przeszło, 0 zakończyło się niepowodzeniem.

    Wszystkie testy zaliczone

    Zdecydowanie konieczne jest pokrycie kodu testami, niezależnie od skali tworzonej aplikacji. Jeszcze raz przypominam, że nawet najmniejsze programy zamieniają się w niezdarne potwory, które wymagają wsparcia i dodania funkcjonalności. Dobrze przetestowany kod to klucz do sukcesu i jakości. Tak, nie jest łatwo od razu zacząć pisać kod odpowiedni do testów automatycznych, ale uwierzcie mi, cała ta męka zaprocentuje w przyszłości. To wszystko na dzisiaj, powodzenia!

    Kiedy nie ma czasu na testy

    Jeśli nie masz czasu, nie ma sensu pisać testów dla prostych funkcji (weź tę samą funkcję trim() z przykładów w artykule); lepiej skupić się na najbardziej krytycznych fragmentach kodu. Tej samej zasady należy przestrzegać pisząc często zmieniany kod. Specyfikacje techniczne działającego projektu często się zmieniają, a niektóre funkcje muszą być stale aktualizowane. Takie zmiany mogą prowadzić do nieprzyjemnych chwil - zmieniony kod dobrze współpracuje z nowymi danymi, ale nie trawi organicznie starych danych. Aby więc nie złapać tutaj awarii, lepiej od razu objąć takie funkcje testami. Pamiętaj o prostej zasadzie – nie ma czasu na zasypywanie testami całego kodu, zajmij się jego najważniejszą częścią.

    Zasady dobrych testów
  • Test powinien być tak prosty, jak to tylko możliwe. Im bardziej złożony test, tym większe prawdopodobieństwo popełnienia błędów;
  • Testy należy pogrupować w moduły, aby łatwiej było później znaleźć błędy i móc przetestować określone części aplikacji;
  • Każdy test powinien być niezależny od innych testów;
  • Zawsze pisz osobny test za każdym razem, gdy znajdziesz błędy;
  • Problemy z phantom.js w systemie Windows

    Tak się złożyło, ale wszystkie przykłady w tym artykule testowałem nie na Linuksie, ale na starym, dobrym Windowsie 7. Okazuje się, że phantom.js ma drobne problemy podczas pracy na systemach korzystających z kilku kart wideo. Na moim laptopie oprócz zintegrowanego układu wideo jest też NVidia i przez phantom.js kategorycznie odmówił reakcji na komendę phantom.exit(). W rezultacie po wykonaniu skryptu proces phantom.js nie zakończył swojej pracy i nadal zawieszał się w pamięci. Okno terminala również przestało odpowiadać na polecenia zamknięcia (ctrl + c nie pomogło).

    Jeśli masz podobny problem i planujesz używać phantom.js w systemie Windows, przygotuj się do wykonania następującego hacka. Otwórz Panel sterowania Nvidia. Znajdź w drzewie pozycję „Ustawienia 3D”. Opcja „Preferowany adapter graficzny” powinna pojawić się po prawej stronie. Domyślnie jego wartość jest ustawiona na „Wybór automatyczny”. Musimy to zmienić na „Wysokowydajny procesor Nvidia” lub „Zintegrowany sprzęt graficzny”. Po tej prostej sztuczce, phantom.js zaczął zachowywać się posłusznie.

  • „Test-Driven JavaScript Development” Cristiana Johansena to jedna z niewielu książek, które przyglądają się JavaScriptowi z punktu widzenia pisania testów;
  • John Resing, Beer Bibo „Sekrety ninja JavaScriptu” to dobra książka, która przyda się przede wszystkim programistom JS o średnim poziomie wyszkolenia. Książka szczegółowo omawia zagadnienia pisania efektywnego kodu między przeglądarkami, niuanse przetwarzania zdarzeń i wiele innych ciekawostek.
  • I jest oficjalnym narzędziem do testowania jQuery. Ale QUnit świetnie nadaje się do testowania dowolnego kodu JavaScript, a nawet może testować backend JavaScript przy użyciu silników takich jak Rhino lub V8.

    Jeśli nie jesteś zaznajomiony z ideą „testowania jednostkowego”, nie martw się – nie ma w tym nic skomplikowanego:

    „Testy jednostkowe lub testy jednostkowe (angielski) testów jednostkowych) to proces w programowaniu, który pozwala sprawdzić poprawność poszczególnych modułów kodu źródłowego programu. Pomysł jest taki, aby pisać testy dla każdej nietrywialnej funkcji lub metody. Dzięki temu można szybko sprawdzić, czy doszło do kolejnej zmiany kodu regresja, czyli na pojawienie się błędów w już przetestowanych miejscach programu, a także ułatwia wykrycie i wyeliminowanie takich błędów.

    Definicja cytowana z Wikipedii. Po prostu wykonaj testy dla każdego bloku funkcjonalnego swojego kodu, a jeśli wszystkie testy zakończą się pomyślnie, możesz być pewien, że nie ma błędów (zależy to głównie od tego, jak starannie zaprojektowano testy).

    Dlaczego powinieneś przetestować swój kod

    Jeśli nigdy wcześniej nie pisałeś testów jednostkowych, prawdopodobnie po prostu umieściłeś swój kod bezpośrednio na serwerze WWW, uruchomiłeś go, obserwowałeś błędy i próbowałeś je naprawić po ich znalezieniu. Ta metoda pracy stwarza wiele problemów.

    Po pierwsze, jest to bardzo żmudna i nudna czynność. Walidacja jest właściwie dość trudną pracą, ponieważ trzeba mieć pewność, że wszystko zostało dopięte na ostatni guzik. W tym procesie istnieje bardzo duże prawdopodobieństwo, że jeden lub dwa punkty zostaną pominięte.

    Po drugie, wszystkiego, co zostało zrobione w celu takich testów, nie można ponownie wykorzystać. Dzięki tej metodzie bardzo trudno jest znaleźć regresję. Co to są regresje? Wyobraź sobie, że napisałeś kod, przetestowałeś go, poprawiłeś wszystkie znalezione błędy i umieściłeś kod na stronie. Następnie użytkownik przesłał opinię na temat nowych błędów i prośbę o nowe funkcje. Wracasz do kodu, naprawiasz błędy i dodajesz nowe funkcje. W takim przypadku może dojść do sytuacji, w której ponownie pojawią się stare błędy, co nazywa się „regresją”. Trzeba wszystko jeszcze raz sprawdzić. I jest szansa, że ​​nie znajdziesz swoich starych błędów. W każdym razie minie trochę czasu, zanim zorientujesz się, że przyczyną problemu jest „regresja”. Korzystając z testów jednostkowych, piszesz test. Po zmodyfikowaniu kodu należy go ponownie przefiltrować przez test. Jeśli nastąpi regresja, niektóre testy zakończą się niepowodzeniem i można łatwo określić, która część kodu zawiera błąd. Ponieważ wiesz, co zmieniłeś, błąd będzie łatwy do naprawienia.

    Kolejną zaletą testów jednostkowych (szczególnie w przypadku tworzenia stron internetowych) jest to, że łatwo jest przetestować kompatybilność między przeglądarkami. Wystarczy przeprowadzić testy w różnych przeglądarkach. Jeśli w przeglądarce zostaną znalezione problemy, możesz je naprawić i ponownie uruchomić test. W rezultacie będziesz mieć pewność, że wszystkie przeglądarki docelowe są obsługiwane, ponieważ wszystkie zostały przetestowane.

    Jak pisać testy jednostkowe w QUnit

    Jak więc bezpośrednio napisać testy jednostkowe w QUnit? Pierwszym krokiem jest instalacja środowiska testowego:

    Zestaw testów QUnit Zestaw testów QUnit

    Kod, który będzie testowany, trafia do pliku myProject.js, a testy do myTests.js. Aby uruchomić testy wystarczy otworzyć plik HTML w przeglądarce. Teraz czas napisać test.

    Podstawą testów jednostkowych jest asercja.

    „Twierdzenie to wyrażenie, które przewiduje wynik zwrócony po wykonaniu kodu. Jeśli przewidywanie jest nieprawidłowe, asercja ma wartość FAŁSZ, co pozwala nam wyciągnąć wnioski na temat występowania błędów.”

    Aby wykonać asercje, należy je umieścić w bloku testowym:

    // przetestuj tę funkcję funkcja isEven(val) ( return val % 2 === 0; ) test("isEven()", funkcja() ( ok(isEven(0), "Zero jest liczbą parzystą"); ok ( isEven(2), „Dwa też jest”); ok(isEven(-4), „A minus cztery to także liczba parzysta”); ok(!isEven(1), „Jeden jest liczbą nieparzystą”); ok(! isEven(-7), "Jak minus siedem jest liczbą nieparzystą"); ))

    Tutaj definiujemy funkcję jest parzysty, który sprawdza parzystość liczby i chcemy mieć pewność, że ta funkcja nie zwróci błędnych wartości.

    Najpierw wywołujemy funkcję test(), który tworzy blok testowy. Pierwszym parametrem jest ciąg znaków, który zostanie wygenerowany w wyniku. Drugi parametr to funkcja zwrotna zawierająca nasze asercje. Ta funkcja wywołania zwrotnego zostanie wywołana raz podczas wykonywania QUnit.

    Napisaliśmy pięć stwierdzeń, z których wszystkie są logiczne. Instrukcja logiczna zakłada, że ​​pierwszy parametr ma wartość PRAWDA. Drugi parametr to komunikat wyświetlany w wyniku.

    Oto co otrzymamy po uruchomieniu testu:

    Wszystkie nasze stwierdzenia zostały pomyślnie potwierdzone, więc możemy założyć, że funkcja jest parzysty() działa zgodnie z oczekiwaniami.

    Zobaczmy, co się stanie, jeśli stwierdzenie będzie fałszywe.

    // przetestuj tę funkcję funkcja isEven(val) ( return val % 2 === 0; ) test("isEven()", funkcja() ( ok(isEven(0), "Zero jest liczbą parzystą"); ok ( isEven(2), „Dwa też jest”); ok(isEven(-4), „A minus cztery to także liczba parzysta”); ok(!isEven(1), „Jeden jest liczbą nieparzystą”); ok(! isEven(-7), "Jak minus siedem jest liczbą nieparzystą"); // Błąd ok(isEven(3), "Trzy jest liczbą parzystą"); ))

    A oto co otrzymamy w wyniku przeprowadzenia testu:


    Oświadczenie zawiera błąd, który popełniliśmy celowo. Ale w twoim projekcie, jeśli jakiś test się nie powiedzie, a wszystkie inne stwierdzenia są poprawne, bardzo łatwo będzie znaleźć błąd.

    Inne stwierdzenia

    ok() nie jest jedyną instrukcją obsługiwaną przez QUnit. Istnieją inne rodzaje asercji, które można wykorzystać podczas pisania testów dla projektów:

    Oświadczenie porównawcze

    Oświadczenie porównawcze równa się() zakłada, że ​​pierwszy parametr (który jest wartością rzeczywistą) jest równoważny drugiemu parametrowi (który jest wartością oczekiwaną). To stwierdzenie jest bardzo podobne do OK(), ale generuje zarówno wartości rzeczywiste, jak i oczekiwane, co znacznie ułatwia debugowanie kodu. Jak również OK(), równa się() może zaakceptować komunikat na wyjściu jako trzeci parametr.

    Więc zamiast

    Test("twierdzenia", funkcja() ( ok(1 == 1, "jeden równa się jednemu"); ))


    Należy stosować:

    Test("twierdzenia", funkcja() ( równa się (1, 1, "jeden równa się jednemu"); ))


    Należy pamiętać, że oczekiwana wartość jest drukowana na końcu linii.

    A jeśli wartości nie są równe:

    Test("twierdzenia", funkcja() ( równa się (2, 1, "jeden równa się jednemu"); ))


    Ten wpis zawiera więcej informacji.

    Twierdzenie porównania używa operatora „==” do testowania parametrów, więc nie może działać z tablicami ani obiektami:

    Test("test", funkcja() ( równa się((), (), "błąd, to są różne obiekty"); równa się((a: 1), (a: 1) , "błąd"); równa się(, , „błąd, to są różne tablice”); równa się (, , „błąd”); ))

    W takich przypadkach QUnit ma potwierdzenie tożsamości.

    Twierdzenie tożsamości

    Twierdzenie tożsamości To samo() wykorzystuje te same parametry co równa się(), ale działa nie tylko z typami pierwotnymi, ale także z tablicami i obiektami. Instrukcje z poprzedniego przykładu przejdą test, jeśli zostaną zmienione z na twierdzenia tożsamości:

    Test("test", funkcja() ( same((), (), "zalicza się, obiekty mają tę samą zawartość"); same((a: 1), (a: 1) , "zalicza"); same( , , "podane, tablice mają tę samą zawartość"); same(, , "podane"); ))

    Proszę to zanotować To samo() używa operatora „===” do porównania, więc wygodnie jest go używać do porównywania wartości specjalnych:

    Test("test", funkcja() ( równa się (0, fałsz, "prawda"); same(0, fałsz, "fałsz"); równa się (null, niezdefiniowane, "prawda"); same(null, niezdefiniowane, " FAŁSZ"); ))

    Struktura instrukcji

    Umieszczanie wszystkich twierdzeń w jednym teście jest bardzo złym pomysłem. Taki test będzie trudny w utrzymaniu i można się pomylić w ocenie wyników jego wykonania. Należy zatem ustrukturyzować test, umieszczając instrukcje w osobnych blokach, z których każdy będzie nakierowany na konkretną grupę funkcji.

    Poszczególne moduły można organizować za pomocą wywołania funkcji moduł:

    Moduł("Moduł A"); test("Test", funkcja() ()); test("Kolejny test", funkcja() ()); moduł("Moduł B"); test("Test", funkcja() ()); test("Kolejny test", funkcja() ());


    W poprzednim przykładzie wszystkie instrukcje zostały wywołane synchronicznie, czyli zostały wykonane jedna po drugiej. W prawdziwym świecie istnieje wiele funkcji asynchronicznych, takich jak żądania lub funkcje AJAX setTimeout() I setInterval(). Jak testujemy tego typu funkcjonalność? QUnit ma specjalny typ testu zwany „testem asynchronicznym”, który jest przeznaczony do testowania asynchronicznego:

    Na początek spróbujmy napisać test w zwykły sposób:

    Test("Test asynchroniczny", funkcja() ( setTimeout(funkcja() ( ok(true); ), 100) ))


    Wygląda na to, że w teście nie ma żadnych twierdzeń. Ponieważ instrukcja została wykonana synchronicznie, ale w momencie wywołania funkcji test był już zakończony.

    Prawidłowy sposób przetestowania naszego przykładu to:

    Test("Test asynchroniczny", funkcja() ( // Przełącz test w tryb pauzy stop(); setTimeout(function() ( ok(true); // Po wywołaniu asercji // kontynuuj test start(); ) , 100) ))


    Korzystaliśmy z funkcji zatrzymywać się() aby zatrzymać test, a po wykonaniu asercji ponownie uruchomić test za pomocą funkcji początek().

    Wywołanie funkcji zatrzymywać się() zaraz po wywołaniu funkcji test() jest bardzo powszechną praktyką. Dlatego QUnit ma specjalny skrót: test asynchroniczny(). Poprzedni przykład można przepisać jako:

    AsyncTest("Test asynchroniczny", funkcja() ( // Test zostaje automatycznie przełączony w tryb "pauza" setTimeout(function() ( ok(true); // Po wywołaniu asercji // kontynuuj uruchamianie testu(); ) , 100) ) )

    Jest jeden punkt, o którym warto pomyśleć: funkcja setTimeout() zawsze wywołuje swoją funkcję wywołania zwrotnego, a jeśli testuje inną funkcję (na przykład wywołanie AJAX). Skąd możesz mieć pewność, że funkcja wywołania zwrotnego zostanie wywołana? Jeśli funkcja return nie zostanie wywołana, funkcja początek() również pozostanie niewywołany i cały test zostanie zawieszony:


    Możesz zorganizować test w następujący sposób:

    // Funkcja niestandardowa funkcja ajax(successCallback) ( $.ajax(( url: "server.php", sukces: sukcesCallback )); ) test("Test asynchroniczny", funkcja() ( // Zatrzymaj test i // zgłoś błąd, jeśli funkcja start() nie zostanie wywołana po 1 sekundzie stop(1000); ajax(function() ( // ...instrukcja asynchroniczna start(); )) ))

    Funkcjonować zatrzymywać się() przesyłana jest wartość limitu czasu. Teraz powiedziano QUnit: „funkcja if początek() nie zostanie wywołany po upływie limitu czasu, test ten należy uznać za niezaliczony.” Teraz cały test nie zostanie zawieszony, a jeśli coś pójdzie nie tak, zostanie wydane ostrzeżenie.

    Rozważmy teraz przypadek wielu funkcji asynchronicznych. Gdzie umieścić funkcję początek()? Musisz umieścić to w funkcji setTimeout():

    // Funkcja niestandardowa funkcja ajax(successCallback) ( $.ajax(( url: "server.php", sukces: sukcesCallback )); ) test("Test asynchroniczny", funkcja() ( // Zatrzymaj zatrzymanie testu(); ajax (funkcja() ( // ...instrukcja asynchroniczna )) ajax(funkcja() ( // ...instrukcja asynchroniczna )) setTimeout(funkcja() ( start(); ), 2000); ))

    Wartość limitu czasu musi być wystarczająca, aby umożliwić zakończenie obu wywołań zwrotnych przed kontynuacją testu. Jeśli jedna z funkcji nie zostanie wywołana, jak możemy ustalić, która? Jest do tego funkcja oczekiwać():

    // Funkcja niestandardowa funkcja ajax(successCallback) ( $.ajax(( url: "server.php", sukces: sukcesCallback )); ) test("Test asynchroniczny", funkcja() ( // Zatrzymaj zatrzymanie testu(); / / Powiedz QUnit, że spodziewamy się wykonania trzech instrukcji: require(3); ajax(function() ( ok(true); )) ajax(function() ( ok(true); ok(true); )) setTimeout( funkcja( ) ( start(); ), 2000); ))

    Przechodzimy do funkcji oczekiwać() liczba instrukcji, które są zaplanowane do wykonania. Jeśli jedno ze stwierdzeń nie powiedzie się, otrzymasz komunikat wskazujący, że coś nie idzie zgodnie z planem.

    Mamy dla Was krótkie nagranie oczekiwać(): musisz przekazać liczbę zaplanowanych instrukcji jako drugi parametr test() Lub test asynchroniczny():

    // Funkcja niestandardowa funkcja ajax(successCallback) ( $.ajax(( url: "server.php", sukces: sukcesCallback )); ) // Poinformuj QUnit, że spodziewamy się wykonania 3 instrukcji test("asynchronous test", 3 , funkcja() ( // Zatrzymaj test stop(); ajax(function() ( ok(true); )) ajax(function() ( ok(true); ok(true); )) setTimeout(function() ( początek (); ), 2000); ))

    Wniosek

    W tym samouczku zapewniliśmy wszystko, czego potrzebujesz, aby rozpocząć pracę z QUnit. Testy jednostkowe to świetna metoda testowania kodu przed jego użyciem. Jeśli nigdy wcześniej nie korzystałeś z żadnych testów, teraz jest czas, aby zacząć.

    Tworzenie efektywnych przypadków testowych może być niezwykle istotne w przypadku dużych projektów, w których zachowanie części aplikacji może z różnych powodów ulec zmianie. Być może najczęstszym problemem jest sytuacja, gdy duża grupa programistów pracuje nad tymi samymi lub powiązanymi modułami. Może to prowadzić do nieplanowanych zmian w zachowaniu funkcji napisanych przez innych programistów. Lub praca pod napiętymi terminami prowadzi do niezamierzonych zmian w krytycznych częściach aplikacji.

    Testowanie aplikacji internetowej zwykle obejmuje wizualną ocenę elementów strony i empiryczną ocenę wydajności funkcjonalności. Innymi słowy podczas poruszania się po sekcjach i wykonywania akcji na elementach dynamicznych.

    Z czasem projekt zostaje zapełniony nową funkcjonalnością, co wydłuża i komplikuje proces sprawdzania jego działania. Do automatyzacji wykorzystuje się testy jednostkowe.

    Istnieją 2 podejścia do budowania scenariuszy testowych:

    • Testowanie Whitebox – pisanie testów opiera się na implementacji funkcjonalności. Te. Sprawdzamy stosując te same algorytmy, na których opiera się praca modułów naszego systemu. Takie podejście nie gwarantuje poprawnego działania systemu jako całości.
    • Testowanie Blackbox – tworzenie skryptów na podstawie specyfikacji i wymagań systemu. Można w ten sposób sprawdzić poprawność wyników całej aplikacji, jednak takie podejście nie pozwala na wyłapanie drobnych i rzadkich błędów.
    Co przetestować

    Może się wydawać, że warto przetestować każdą zaimplementowaną funkcję. Nie jest to do końca prawdą. Pisanie testów zajmuje czas programisty, dlatego aby zoptymalizować proces tworzenia aplikacji, warto przygotować testy tylko dla funkcji złożonych, krytycznych lub tych, które zależą od wyników innych modułów systemu. Obejmij niejednoznaczną logikę testami, które mogą potencjalnie powodować błędy. Warto także stworzyć testy dla tych fragmentów kodu, które w przyszłości mają zostać zoptymalizowane, aby po procesie optymalizacji móc zweryfikować, czy zostały one wykonane poprawnie.

    Ogólnie rzecz biorąc, niezwykle ważne jest oszacowanie kosztów testowania w kontekście napiętych terminów rozwoju. Oczywiście, jeśli nie jesteś ograniczony czasowo, możesz pozwolić na pokrycie każdej funkcji testami. Ale z reguły rozwój odbywa się pod dużą presją czasu, więc zadaniem analityka lub doświadczonego programisty jest zrozumienie, gdzie konieczne jest testowanie. Dodatkowo pisanie testów zwiększa koszt projektu.

    Można zatem sformułować 3 przypadki, w których zastosowanie testów jednostkowych jest uzasadnione:

    1) Jeżeli testy pozwalają na szybszą identyfikację błędów niż przy normalnym ich wyszukiwaniu.

    2) Skróć czas debugowania

    3) Umożliwia testowanie często zmienianego kodu.

    Z 3 głównych komponentów frontendu (HTML, CSS, JavaScript) być może przetestowania wymaga jedynie kod JavaScript. CSS jest testowany wyłącznie wizualnie, gdzie programista/tester/klient przegląda GUI w różnych przeglądarkach. Znacznik HTML jest sprawdzany tą samą metodą.

    Jak przetestować

    Konstruując scenariusze testowe należy kierować się następującymi zasadami:

    • Twoje testy powinny być tak proste, jak to możliwe. Wtedy będzie większe prawdopodobieństwo, że na wyniki jego wdrożenia będzie miał wpływ sam błąd, który próbujesz powtórzyć.
    • Rozłóż duże testy jednostkowe. Lepiej jest znaleźć konkretną lokalizację błędu.
    • Uniezależnij testy. Wynik jednego testu nie powinien w żaden sposób zależeć od wyników innego.
    • Wyniki badań muszą być w pełni powtarzalne i oczekiwane. Za każdym razem, gdy uruchamiasz test ponownie, wynik powinien być taki sam jak ostatnim razem.
    • W przypadku jakiegokolwiek błędu w działaniu aplikacji należy utworzyć skrypt testowy. W ten sposób będziesz mieć pewność, że błąd został rzeczywiście naprawiony i nie będzie widoczny dla użytkowników.
    Co przetestować

    Istnieje kilka bibliotek do testowania jednostkowego kodu js. Być może najczęstszym jest QUnit. Aby przeprowadzić testy jednostkowe przy użyciu tej biblioteki, będziemy musieli stworzyć „piaskownicę” – prostą stronę HTML, w której zostanie połączona biblioteka do testowania, kod, który należy przetestować, oraz same testy.

    Funkcje do testów:

    (funkcja() ( window.stepen = funkcja(int) ( var wynik = 2; for (var i = 1; i< int; i ++) { result = result * 2; } return result; } window.returnFunc = function() { return "ok"; } })();

    Lista testów:

    Test("stepen()", funkcja() ( równa(stepen(2), 4, "2^2 - metoda równa"); ok(stepen(3) === 8, "2^3 - metoda ok" ); deepEqual(stepen(5), 32, "2^5 - metoda deepEqual"); )); asyncTest("returnFunc()", funkcja() ( setTimeout(funkcja() ( równości(returnFunc(), "ok", "Test funkcji asynchronicznej"); start(); ), 1000); ));

    Jak widać, QUnit obsługuje 3 funkcje umożliwiające porównanie wyników wykonania kodu z oczekiwanymi:

    • ok() – uznaje test za udany, jeśli zwrócony wynik = true
    • równości() – porównuje wynik z oczekiwanym
    • deepEqual() – porównuje wynik z oczekiwanym, sprawdzając jego typ

    Wynik wykonania:

    Jak widać biblioteka QUnit testuje kod dla kilku przeglądarek jednocześnie.

    Dostępnych jest wiele innych bibliotek testów jednostkowych. Jednak koncepcja konstruowania w nich scenariuszy testowych jest taka sama, dlatego gdy już zrozumiesz jeden, nie będzie Ci trudno przejść na inny.

    Ważne do zapamiętania

    Cechą współczesnego kodu js jest jego asynchroniczne wykonanie. Biblioteki testowe zazwyczaj mają możliwość przeprowadzania testów asynchronicznych. Ale na przykład, jeśli próbujesz przetestować funkcję, która, powiedzmy, wysyła żądanie get do backendu i zwraca z niego odpowiedź, to aby przeprowadzić testy, będziesz musiał zatrzymać wątek za pomocą funkcji stop(), uruchomić testowaną funkcję, a następnie zrestartuj wątek za pomocą metody start(), „zawijając go” w setTimeout(). Te. należy ustawić określony okres czasu, w którym funkcja powinna zakończyć wykonywanie. Musisz dokładnie wybrać czas trwania tego segmentu. z jednej strony długie działanie metody może być cechą lub wręcz koniecznością konkretnej implementacji funkcjonalności aplikacji lub nieprawidłowym zachowaniem.

    Testowanie aplikacji szkieletowych

    Dla przykładu testowania aplikacji napisanych przy użyciu Backbone.js skorzystamy z projektu opisanego w.

    Możesz użyć testów jednostkowych, aby sprawdzić:

    • Prawidłowe tworzenie modeli i kontrolerów
    • Poprawność danych w modelach
    • Wykonanie metod kontrolera (w tym celu muszą zwrócić wynik)
    • Ładowanie wyświetleń powiodło się

    Kod testowy:

    Test("Backbone.js", funkcja() ( ok(przykład, "Sprawdzanie przestrzeni nazw"); ok(przykład.routers.app, "Sprawdzanie routera"); ok(przykład.core.pageManager.open("czat") , "Test otwarcia strony (wywołanie metody kontrolera)") ok(sample.core.state, "Sprawdzanie modelu"); równości(sample.core.state.get("content"), "sintel", "Test pobierania danych modelu "); stop(); ok(funkcja() ( $.ajax(( url: "app/templates/about.tpl", typ danych: "tekst")).done(funkcja(dane) ( self.$el. html(data); zwróć dane; )) ), „Sprawdzanie ładowania szablonu”); setTimeout(function() ( start(); ), 1000); ));

    Wynik pracy z błędami testowymi:

    Automatyzacja przebiegu testowego

    Zazwyczaj wdrażanie aplikacji jest zadaniem, które należy wykonywać dość często podczas intensywnego rozwoju. Dlatego operacja ta jest zwykle zautomatyzowana. W naszej pracy wykorzystujemy Jenkins, narzędzie do ciągłej integracji. Pomysł polega na połączeniu wdrożenia za pośrednictwem Jenkinsa z testami automatycznymi.

    Testy QUnit uruchamiane są w przeglądarce. Pomoże nam obejść tę funkcję Phantomjs, oprogramowanie emulujące działanie przeglądarki. Twórcy phantomjs udostępnili już skrypt do uruchamiania testów QUnit, jednak musiał on zostać nieco zmodyfikowany, aby działał poprawnie.

    /** * Poczekaj, aż warunek testowy zostanie spełniony lub nastąpi przekroczenie limitu czasu. * Przydatne do oczekiwania * na odpowiedź serwera lub do wystąpienia zmiany interfejsu użytkownika (fadeIn itp.). * * @param testFx warunek JavaScript, którego wynikiem jest wartość logiczna, * można go przekazać jako ciąg znaków (np.: "1 == 1" lub * "$("#bar").is(:visible)" lub * jako funkcja zwrotna * @param onReady co zrobić, gdy spełniony jest warunek testFx, * można go przekazać jako ciąg znaków (np.: "1 == 1" lub * "$("#bar").is ( ":visible")" lub * jako funkcję wywołania zwrotnego. * @param timeOutMillis maksymalny czas oczekiwania. Jeśli nie * określono, używane są 3 sekundy. */ funkcja waitFor(testFx, onReady, timeOutMillis) ( var maxtimeOutMillis = timeOutMillis ?timeOutMillis: 3001, //< Default Max Timout is 3s start = new Date().getTime(), condition = false, interval = setInterval(function() { if ((new Date().getTime() - start < maxtimeOutMillis) && !condition) { // If not time-out yet and condition not yet fulfilled condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()); //< defensive code } else { if(!condition) { // If condition still not fulfilled // (timeout but condition is "false") console.log(""waitFor()" timeout"); phantom.exit(1); } else { // Condition fulfilled (timeout and/or condition is //"true") console.log(""waitFor()" finished in " + (new Date().getTime() - start) + "ms."); typeof(onReady) === "string" ? eval(onReady) : onReady(); //< Do what it"s supposed to do once the // condition is fulfilled clearInterval(interval); //< Stop this interval } } }, 100); // repeat check every 250ms }; }; if (phantom.args.length === 0 || phantom.args.length >2) console.log("Użycie: URL run-qunit.js"); fantom.exit(); ) var strona = nowa strona internetowa(); // Kieruj wywołania „console.log()” z kontekstu strony // do głównego kontekstu Phantom (tj. bieżącego „tego”) page.onConsoleMessage =function(msg) ( console.log(msg); ); page.open(phantom.args, funkcja(status)( if (status !== "sukces") ( console.log("Nie można uzyskać dostępu do sieci"); phantom.exit(); ) else ( waitFor(funkcja() (zwróć stronę.evaluate(funkcja())( var el = document.getElementById("qunit-testresult"); if (el && el.innerText.match("ukończono")) (zwróć true; ) return false; )) ; ), funkcja())( var nieudanyNum = page.evaluate(funkcja())( var el = document.getElementById("qunit-testresult"); console.log(el.innerText); try ( return document.getElementsByClassName( "niepowodzenie" ).innerHTML.length; ) catch (e) ( return 0; ) return 10000; )); phantom.exit((parseInt(failedNum, 10) > 0) ? 1: 0); )); ) ) );

    Aby wysyłać komunikaty o wynikach do konsoli, musisz dodać funkcję rejestrowania do skryptu testowego.

    Na stronie dostępne są już testy wiedzy z następujących tematów: HTML, CSS, JavaScript, PHP, SQL.

    Każdy test składa się z 10 pytań na określony temat. W każdym pytaniu starałem się poruszyć najróżniejsze obszary zastosowań danego języka, aby jak najdokładniej sprawdzić Twój poziom wiedzy.

    Oczywiście wszystkie badania są bezpłatne i może je wykonać każdy.

    Procedura testowa:

  • Kliknij link „Rozpocznij testowanie” odpowiadający odpowiedniemu testowi.
  • Odpowiedz na postawione pytania, wybierając jedyną poprawną opcję.
  • Po zakończeniu testu zobaczysz swój wynik, liczbę błędów, a także analizę każdego pytania z testu.
  • Uwaga! Nie będziesz mógł wrócić do poprzedniego pytania, więc zastanów się zanim odpowiesz.

    Aktualnie dostępne testy
  • HTML
    • Łączna liczba wykonanych testów: 75 424 osób
    • Średni wynik: 2,83 na 5 punktów.

    Test podstaw HTML. Będziesz potrzebować znajomości podstawowych tagów HTML, a także umiejętnego ich wykorzystania. Konieczne jest także zrozumienie cech standardu XHTML 1.1.

  • CSS
    • Łącznie wykonano test: 32828 osób
    • Średni wynik: 3,37 na 5 punktów.

    Test sprawdza znajomość podstaw CSS. Aby pomyślnie przejść test, należy znać główne typy selektorów (ich składnię), znać podstawowe właściwości i ich możliwe wartości, a także znać przeznaczenie najpopularniejszych pseudoelementów.

  • JavaScript
    • Łącznie wykonano test: 24845 osób
    • Średni wynik: 3,31 na 5 punktów.

    Ten test sprawdza Twoją wiedzę na temat języka JavaScript. Pytania testowe dotyczą różnych obszarów zastosowań tego języka. Istnieje wiele pytań dotyczących zrozumienia „drobnych” niuansów. W przeciwnym razie musisz znać podstawowe rzeczy: pracę ze zmiennymi, podstawowe funkcje JavaScript, priorytety operacji itp.

  • PHP
    • Łącznie wykonano test: 33 239 osób
    • Średni wynik: 3,03 na 5 punktów.

    Ten test sprawdza Twoją wiedzę na temat języka PHP. Wymagana jest znajomość podstawowych konstrukcji PHP, pracy ze zmiennymi, sesjami, implementacji przekierowań i innych standardowych rzeczy.
    Uprzejmie prosimy: Test zawiera wiele pytań typu: „Co wyświetli skrypt?” Proszę nie kopiować i nie sprawdzać. Bądź ze sobą szczery.

  • SQL-a
    • Łącznie wykonano test: 18 014 osób
    • Średni wynik: 3,28 na 5 punktów.

    Ten test sprawdza Twoją wiedzę na temat języka zapytań SQL. Pytania dotyczą jedynie najbardziej podstawowej wiedzy o tym języku, bez jakiejkolwiek głębszej wiedzy. Będziesz potrzebować znajomości najbardziej podstawowych zapytań SQL, a także umiejętnego ich wykorzystania.