Sztuka testowania układu za pomocą Galen Framework

Opublikowany: 2022-03-10
Szybkie podsumowanie ↬ Podczas projektowania graficznego interfejsu użytkownika zawsze pozostaje otwarte pytanie: Jak możemy zautomatyzować jego testowanie? A jak dbamy o to, aby układ strony był responsywny i poprawnie wyświetlał się na wszelkiego rodzaju urządzeniach o różnych rozdzielczościach? Dodając do tego komplikacje wynikające z dynamicznych treści, wymagań dotyczących internacjonalizacji i lokalizacji, staje się to prawdziwym wyzwaniem. W tym artykule poprowadzę Cię przez interesującą nową technikę testowania układu. Korzystając z Galen Framework , zapewnię szczegółowy samouczek do pisania znaczących uogólnionych testów układu, które można wykonać w dowolnej przeglądarce i na dowolnym urządzeniu, a jednocześnie wykorzystać jako pojedyncze źródło prawdy w dokumentacji projektowej.

Podczas projektowania graficznego interfejsu użytkownika zawsze pozostaje otwarte pytanie: jak możemy zautomatyzować jego testowanie? A jak dbamy o to, aby układ strony był responsywny i poprawnie wyświetlał się na wszelkiego rodzaju urządzeniach o różnych rozdzielczościach? Dodając do tego komplikacje wynikające z dynamicznych treści, wymagań dotyczących internacjonalizacji i lokalizacji, staje się to prawdziwym wyzwaniem.

W tym artykule poprowadzę Cię przez interesującą nową technikę testowania układu. Korzystając z Galen Framework, zapewnię szczegółowy samouczek dotyczący pisania znaczących uogólnionych testów układu, które można wykonać w dowolnej przeglądarce i na dowolnym urządzeniu, a jednocześnie wykorzystać jako pojedyncze źródło prawdy w dokumentacji projektowej.

Dalsze czytanie na SmashingMag:

  • Tworzenie oparte na testach wizualnych do projektowania responsywnych interfejsów
  • Podstawy automatyzacji testów aplikacji, gier i sieci mobilnej
  • Różnorodne frameworki do automatyzacji testów dla aplikacji natywnych React

Pokażę również, w jaki sposób wymyśliłem zoptymalizowany test dla strony z wiadomościami na naszej stronie z ogłoszeniami, Marktplaats. Dowiemy się, jak rozszerzyć składnię Galena o nasz własny język, jak ulepszyć kod testowy i jak zamienić rutynę testowania układu w grafikę.

Więcej po skoku! Kontynuuj czytanie poniżej ↓

Wprowadzenie do Galen Framework

Galen Framework został omówiony rok temu w artykule „Rozwój oparty na testach wizualnych do projektowania responsywnych interfejsów”. W tym czasie jego składnia była ograniczona. Od tego czasu znacznie się poprawił i otrzymał wiele nowych funkcji, którym przyjrzymy się tutaj.

Jeśli nie znasz Galen Framework, jest to narzędzie do responsywnego testowania układu i testowania w różnych przeglądarkach, z własnym językiem testowym o nazwie Galen Specs. Jest oparty na Selenium WebDriver, a także ma bogate API JavaScript, które umożliwia bezpośrednią pracę z WebDriver. Ponieważ masz kontrolę nad WebDriverem, możesz uruchamiać testy w dowolnej przeglądarce, w chmurze (SauceLabs, BrowserStack, PerfectoMobile itp.) lub na prawdziwych urządzeniach mobilnych przy użyciu Appium.

Instalowanie i uruchamianie

Konfiguracja Galen Framework jest łatwa. Wystarczy wykonać następujące polecenie, aby zainstalować go przez npm:

 npm install -g galenframework-cli

Jeśli nie używasz npm, po prostu pobierz najnowsze archiwum Galen Framework, rozpakuj pakiet i postępuj zgodnie z instrukcjami instalacji.

Po zainstalowaniu Galen Framework można uruchomić na różne sposoby. Na przykład możesz użyć polecenia check , aby uruchomić szybki test pojedynczej strony. W przypadku tego polecenia musisz dostarczyć plik .gspec z walidacjami układu, a następnie możesz go wywołać w ten sposób:

 galen check loginPage.gspec --url https://example.com --size 1024x768 --include desktop --htmlreport reports

To polecenie uruchomi przeglądarkę, otworzy określony adres URL, zmieni rozmiar okna przeglądarki do 1024 × 768 pikseli i wykona wszystkie walidacje zadeklarowane w pliku loginPage.gspec . W rezultacie otrzymasz szczegółowy raport HTML.

Zarządzanie zestawami testowymi

W prawdziwym świecie rzeczywista aplikacja internetowa nie składa się wyłącznie ze statycznych stron. Dość często będziesz musiał wykonać pewne czynności, aby dostać się do miejsca, które chcesz sprawdzić. W tym przypadku Galen oferuje zestawy testów JavaScript i GalenPages JavaScript API do implementacji modelu obiektowego strony. Oto prosty przykład testu JavaScript Galen:

 test("Home page", function() { var driver = createDriver("https://galenframework.com", "1024x768"); checkLayout(driver, "homePage.gspec", ["desktop"]); });

A oto implementacja modelu obiektowego strony dla strony logowania, zaczerpnięta z rzeczywistego projektu.

 WelcomePage = $page("Welcome page", { loginButton: "#welcome-page .button-login" }); LoginPage = $page("Login page", { username: "input[name='login.username']", password: "input[name='login.password']", loginButton: "button.button-login" loginAs: loggedFunction ("Log in as ${_1.username} with password ${_1.password}", function(user) { this.username.typeText(user.username); this.password.typeText(user.password); this.loginButton.click(); }) }); test("Login page", function() { var driver = createDriver("https://testapp.galenframework.com", "1024x768"); var welcomePage = new WelcomePage(driver).waitForIt(); welcomePage.loginButton.click(); new LoginPage(driver).waitForIt(); checkLayout(driver, "loginPage.gspec", ["desktop"]); });

Dla zaawansowanych zastosowań radzę zajrzeć do projektu Galen Bootstrap. Jest to rozszerzenie JavaScript zbudowane specjalnie dla Galena. Zapewnia dodatkowe funkcje do testowania interfejsu użytkownika i łatwiejszy sposób konfigurowania przeglądarek i wykonywania złożonych zestawów testów.

Prosty test układu

Zacznę od wprowadzenia prostego testu układu w Galen Framework. Następnie przejdę do zaawansowanych przypadków użycia i pokażę, jak rozszerzyć składnię Galen Specs. W tym celu przyjrzymy się nagłówkowi z ikoną i podpisem:

Ikona i podpis
Ikona i podpis (Wyświetl dużą wersję)

W kodzie HTML może to wyglądać mniej więcej tak:

 <body> <!-- … --> <div> <img class="header-logo" src="/imgs/header-logo.png"/> <h1>My Blog</h1> </div> <!-- … --> </body>

Najprostsza forma testu układu Galena wyglądałaby mniej więcej tak. Najpierw musimy zadeklarować obiekty za pomocą selektorów CSS.

 @objects header #header icon #header img caption #header h1

Następnie deklarujemy sekcję testową o znaczącej nazwie i umieszczamy pod nią wszystkie nasze walidacje.

 = Icon and Caption = icon: left-of caption 10 to 15px width 32px height 32px inside header 10px top caption: aligned horizontally all header inside header

Tutaj przetestowaliśmy dwa elementy nagłówka: ikonę i podpis. Wszystkie walidacje wymienione pod ikoną i elementami podpisu są w rzeczywistości standardowymi specyfikacjami Galen. Te specyfikacje są podstawowymi elementami konstrukcyjnymi, za pomocą których można zbudować własne rozwiązanie do testowania układu. Każda specyfikacja weryfikuje pojedynczą właściwość (taką jak szerokość, wysokość, tekst), względne położenie (takie jak wewnątrz, z lewej, powyżej) lub piksele na zrzucie ekranu (takie jak schemat kolorów, obraz).

Testowanie wielu elementów za pomocą pętli forEach

W poprzednim przykładzie przedstawiono prosty scenariusz. Zobaczmy, jak poradzimy sobie z bardziej skomplikowaną sytuacją: z poziomym menu. Najpierw wypróbujmy prostą technikę testowania układu.

Menu poziome
Menu poziome (Wyświetl dużą wersję)

Zacznij od dopasowania wielu elementów na stronie. Za pomocą poniższego kodu nakazujemy Galenowi wyszukanie elementów pasujących do #menu ul li CSS.

 @objects menu #menu item-* ul li

Później możemy odwoływać się do tych elementów pod nazwami takimi jak menu.item-1 i menu.item-2 i iterować po wszystkich elementach menu za pomocą pętli @forEach .

 = Menu = menu.item-1: inside menu 0px top left bottom @forEach [menu.item-*] as menuItem, next as nextItem ${menuItem}: left-of ${nextItem} 0px aligned horizontally all ${nextItem}

Jak widać, mimo że samo sprawdzenie nie jest tak skomplikowane, kod stał się już mniej intuicyjny. Wyobraź sobie, że w naszych testach mamy więcej podobnego kodu. W pewnym momencie stanie się niemożliwym do utrzymania bałaganem. Powinien być sposób, aby to poprawić.

Nowe podejście do testowania układu

Jeśli pomyślisz o poprzednim przykładzie, wydaje się, że możemy wyrazić układ w jednym lub dwóch zdaniach. Na przykład możemy powiedzieć coś w stylu: „Wszystkie pozycje menu powinny być wyrównane poziomo, bez marginesów pomiędzy nimi. Pierwsza pozycja menu powinna znajdować się po lewej stronie menu, bez marginesu.” Ponieważ sformułowaliśmy zdania, aby wyjaśnić pożądany układ, dlaczego nie możemy ich po prostu użyć w naszym kodzie? Wyobraź sobie, że moglibyśmy napisać kod w ten sposób:

 = Menu = |first menu.item-* is in top left corner of menu |menu.item-* are aligned horizontally next to each other

W rzeczywistości jest to prawdziwy działający kod, skopiowany z mojego projektu. W tych dwóch ostatnich wierszach (zaczynając od potoku, | ) wywołujemy funkcje niestandardowe, które zbierają swoje argumenty poprzez parsowanie tych dwóch instrukcji. Oczywiście powyższy przykład nie będzie działał tak, jak jest. Aby to się skompilowało, musimy zaimplementować procedury obsługi dla tych dwóch instrukcji. Wrócimy do tej implementacji później.

Kluczowym punktem w powyższym przykładzie jest to, że testowanie układu przesunęło się z testowania opartego na obiektach do testowania opartego na wyrażeniach . Może nie jest to oczywiste na tak drobnym przykładzie, ale na pewno jest zauważalne na większą skalę. Dlaczego więc jest to ważne? Krótka odpowiedź brzmi, że zmienia to nasz sposób myślenia i wpływa na sposób, w jaki projektujemy nasze oprogramowanie i piszemy dla niego testy.

Stosując tę ​​technikę, nie traktujemy naszej strony jako zbioru obiektów z określonymi relacjami między nimi. Nie testujemy właściwości CSS poszczególnych elementów. I unikamy pisania skomplikowanego, niebanalnego kodu. Zamiast tego staramy się myśleć o typowych wzorcach układu i znaczących stwierdzeniach. Zamiast osobno testować pozycję menu 1, pozycję menu 2 i tak dalej, stosujemy ogólne stwierdzenia, które:

  • są powtarzalne na innych elementach;
  • nie zawierają zakodowanych na stałe wartości pikseli;
  • są stosowane do abstrakcji, a nie do konkretnych obiektów;
  • i wreszcie mają sens, kiedy je czytamy.

Jak to działa

Pozwólcie, że wyjaśnię mechanizm wyrażeń niestandardowych układów graficznych na tym prostym przykładzie:

Prosty szkic
Prosty szkic (Zobacz w dużej wersji)

W tym przykładzie mamy sprawdzić, czy przycisk rozciąga się do panelu, bez marginesu po lewej lub prawej stronie. Bez niestandardowych reguł możemy podejść do tego na różne sposoby, ja jednak wolę następujące rozwiązanie:

 button: inside some_panel 0px left right

Powyższy kod daje nam elastyczność w deklarowaniu niestandardowego marginesu po bokach, a także domyślnie testuje, czy przycisk jest całkowicie zawarty w panelu. Minusem jest to, że nie jest zbyt czytelny, dlatego zamierzam umieścić tę walidację za button stretches to some_panel . Aby to zadziałało, musimy napisać niestandardową regułę taką jak ta:

 @rule %{elementName} stretches to %{parentName} ${elementName}: inside ${parentName} 0px left right

Otóż ​​to. Teraz możemy umieścić to w naszym teście w jednym wierszu:

 | button stretches to some_panel

Jak widać, reguła ta przyjmuje dwa argumenty: elementName i parentName . Pozwala nam to zastosować go również do innych elementów. Wystarczy zastąpić nazwy tych dwóch obiektów.

 | login_panel stretches to main_container | header stretches to screen | footer stretches to screen # etc.

Wdrażanie własnego języka testowego

Wróćmy do początkowych przykładów wyrażeń układu dla menu poziomego.

 = Menu = | first menu.item-* is in top left corner of menu | menu.item-* are aligned horizontally next to each other

Pierwszą regułę możemy zaimplementować w następujący sposób:

 @rule first %{itemPattern} is in %{cornerSides} corner of %{parentElement} @if ${count(itemPattern) > 0} ${first(itemPattern).name}: inside ${parentElement} 0px ${cornerSides}

W naszym przykładzie przeanalizuje argumenty w następujący sposób:

  • itemPattern = menu.item-*
  • cornerSides = top left róg
  • parentElement = menu

Ponieważ skończyliśmy z pierwszym wyrażeniem, możemy przejść do następnego. W drugim wyrażeniu mamy przetestować wyrównanie wszystkich elementów menu w poziomie. Proponuję trzy proste kroki:

  1. Znajdź wszystkie pozycje menu.
  2. Iteruj przez wszystkie aż do przedostatniego elementu.
  3. Sprawdź, czy element n znajduje się na lewo od elementu n+1 i czy ich górna i dolna krawędź są wyrównane.

Aby to zadziałało, musimy wziąć pętlę @forEach i specyfikacje left-of i aligned . Na szczęście w Galenie możesz odwoływać się do poprzedniego lub następnego elementu w pętli. W przypadku, gdy zadeklarowałeś odwołanie do następnego elementu, będzie ono iterować tylko do przedostatniego elementu, co jest dokładnie tym, czego potrzebujemy.

 @rule %{itemPattern} are aligned horizontally next to each other @forEach [${itemPattern}] as item, next as nextItem ${item}: left-of ${nextItem} 0px aligned horizontally all ${nextItem}

Możesz zapytać, co jeśli będziemy musieli określić margines w naszym teście (taki jak ~ 20px lub 10 to 20px )? Następnie sugeruję albo zaimplementować oddzielną regułę, albo rozszerzyć istniejącą, aby obsługiwała argument %{margin} .

 @rule %{itemPattern} are aligned horizontally next to each other with %{margin} margin @forEach [${itemPattern}] as item, next as nextItem ${item}: left-of ${nextItem} ${margin} aligned horizontally all ${nextItem}

Otóż ​​to! Stworzyliśmy ogólne wyrażenie, które pomaga nam sprawdzić poprawność menu poziomego. Jednak ze względu na jego elastyczność możemy zrobić znacznie więcej. Możemy go użyć do przetestowania dowolnych innych elementów na stronie. Możemy go nawet użyć do przetestowania wyrównania dwóch przycisków:

Przyciski formularza
Przyciski formularzy (Zobacz w powiększonej wersji)
 | menu.item-* are aligned horizontally next to each other with 0px margin | submit_button, cancel_button are aligned horizontally next to each other with 20px margin

Możesz zauważyć w tych dwóch przykładach, że zadeklarowaliśmy pierwszy argument na dwa różne sposoby. W pierwszym wyrażeniu pierwszym argumentem jest “menu.item-*” , a w drugim wyrażeniu jest on zadeklarowany jako “submit_button, cancel_button” . Jest to możliwe, ponieważ pętla @forEach pozwala nam na użycie rozdzielonej przecinkami listy obiektów wraz z operatorem gwiazdy. Ale nadal nie skończyliśmy z refaktoryzacją. Moglibyśmy jeszcze bardziej ulepszyć kod i uczynić go bardziej czytelnym. Jeśli utworzymy grupy dla pozycji menu i przycisków formularza logowania, możemy wykonać coś takiego:

 @groups menu_items menu_item-* login_form_buttons submit_button, cancel_button = Testing login page = | &menu_items are aligned horizontally next to each other with 0px margin | &login_form_buttons are aligned horizontally next to each other with 20px margin

W tym przypadku musimy użyć symbolu & , który oznacza deklarację grupy. To już dobry test. Po pierwsze to po prostu działa i jesteśmy w stanie przetestować to, czego potrzebujemy. Ponadto kod jest przejrzysty i czytelny. Gdyby inna osoba zapytała Cię, jak powinna wyglądać strona logowania i jakie są wymagania projektowe, możesz powiedzieć jej, aby spojrzała na test.

Jak widać, implementacja wyrażeń niestandardowych dla złożonych wzorców układu nie jest tak naprawdę wielkim problemem. Na początku może to być trudne, ale nadal przypomina coś w rodzaju działalności twórczej.

Marginesy dynamiczne

Spójrzmy na inny rzadki wzór układu, który możesz czasem znaleźć na różnych stronach internetowych. Co by było, gdybyśmy chcieli sprawdzić, czy elementy mają równe odległości od siebie? Spróbujmy zaimplementować w tym celu inną regułę, ale tym razem za pomocą implementacji JavaScript. Proponuję takie stwierdzenie: “box_item-* are aligned horizontally next to each other with equal distance” . Będzie to trochę trudne, ponieważ nie znamy marginesu między elementami i nie możemy po prostu zakodować wartości pikseli na sztywno. Dlatego pierwszą rzeczą, którą musimy zrobić, to pobrać rzeczywisty margines między pierwszym a ostatnim elementem.

Równie odległy
Równie odległe (Zobacz w dużej wersji)

Gdy uzyskamy ten margines, możemy go zadeklarować w pętli @forEach , podobnie jak robiliśmy to wcześniej. Proponuję zaimplementować tę zasadę za pomocą API JavaScript, ponieważ wymagana logika jest nieco bardziej złożona niż we wszystkich naszych poprzednich przykładach. Utwórzmy plik o nazwie my-rules.js i wpiszmy ten kod:

 rule("%{objectPattern} are aligned horizontally next to each other with equal margin", function (objectName, parameters) { var allItems = findAll(parameters.objectPattern), distance = Math.round(Math.abs(allItems[1].left() - allItems[0].right())), expectedMargin = (distance - 1) + " to " + (distance + 1) + "px"; if (allItems.length > 0) { for (var i = 0; i < allItems.length - 1; i += 1) { var nextElementName = allItems[i + 1].name; this.addObjectSpecs(allItems[i].name, [ "aligned horizontally all " + nextElementName, "left-of " + nextElementName + " " + expectedMargin ]); } } });

W naszym kodzie testowym użyjemy go tak:

 @script my-rules.js # … = Boxes = | box_item-* are aligned horizontally next to each other with equal distance

Jak widać, w Galen Framework mamy do wyboru dwa języki podczas implementacji reguł: Galen Specs i JavaScript. W przypadku prostych wyrażeń, Galen Specs jest łatwiejszy w użyciu, ale w przypadku skomplikowanych zawsze wybieram JavaScript. Jeśli chcesz dowiedzieć się więcej o regułach JavaScript, zapoznaj się z dokumentacją.

Dodatki Galena

Mając wystarczająco dużo zabawy z różnymi wzorcami układu, zdałem sobie sprawę, że wszystkie te zasady Galena można łatwo zastosować w dowolnym innym projekcie testowym. To dało mi pomysł na skompilowanie najpopularniejszych wyrażeń układu do własnej biblioteki. W ten sposób doszedłem do stworzenia projektu Galen Extras. Poniżej znajduje się kilka przykładów możliwości tej biblioteki:

 | header.icon should be squared | amount of &menu_items should be > 3 | &menu_items are aligned horizontally next to each other | &list_items are aligned vertically above each other with equal distance | every &menu_item is inside menu 0px top and has width > 50px | first &menu_item is inside menu 0px top left | &menu_items are rendered in 2 column table | &menu_items are rendered in 2 column table, with 0 to 1px vertical and 10px horizontal margin | &login_form_elements sides are vertically inside content_container with 20px margin login_panel: | located on the left side of panel and takes 70 % of its width # etc …

Biblioteka Galen Extras zawiera wiele wzorców układu, które często można znaleźć na stronach internetowych, i aktualizuję ją, gdy tylko znajdę przydatny wzorzec. Po skonfigurowaniu tej biblioteki postanowiłem wypróbować ją w prawdziwym projekcie testowym.

Testowanie aplikacji do przesyłania wiadomości

Obecnie pracuję jako inżynier oprogramowania w Marktplaats. W pewnym momencie postanowiłem wykorzystać całe zdobyte doświadczenie w prawdziwym projekcie. Musiałem przetestować stronę z wiadomościami w naszej witrynie. Oto jak to wygląda:

Strona wiadomości
Strona z wiadomościami (Wyświetl dużą wersję)

Szczerze mówiąc, wdrażanie testów dla takich stron zawsze wydawało mi się trochę przerażające, zwłaszcza testy układu. Ale z biblioteką Galen Extras wszystko poszło całkiem gładko i wkrótce udało mi się wymyślić ten kod:

 @import ../selected-conversation.gspec @groups (message, messages) messenger.message-* first_two_messages messenger.message-1,messenger.message-2 first_message messenger.message-1 second_message messenger.message-2 third_message messenger.message-3 (message_date_label, message_date_labels) messenger.date_label-* first_date_label messenger.date_label-1 second_date_label messenger.date_label-2 = Messages panel = = Messages and Date labels = |amount of visible &message_date_labels should be 1 |first &message_date_label has text is "17 november 2015" |amount of visible &messages should be 3 |&first_two_messages should be located at the left inside messenger with ~ 20px margin |&third_message should be located at the right inside messenger with ~ 20px margin |&messages are placed above each other with 10 to 15px margin |text of all &messages should be ["Hi there!", "I want to buy something", "Hello! Sure, it's gonna be 100 euros"] = Styling = |&first_two_messages should be styled as others message |&third_message should be styled as own message

Wyodrębnianie zakresów pikseli

Test wyglądał OK: był kompaktowy i czytelny, ale wciąż daleki od ideału. Naprawdę nie podobały mi się wszystkie te definicje marginesów ( ~ 20px , 10 to 15px ). Niektóre z nich się powtarzały i trudno było zrozumieć, za czym każdy z nich reprezentował. Dlatego postanowiłem ukryć każdy margines za znaczącą zmienną.

 # ... @set messages_side_margin ~ 20px messages_vertical_margin 10 to 15px = Messages panel = = Messages and Date labels = |amount of visible &message_date_labels should be 1 |first &message_date_label has text is "17 november 2015" |amount of visible &messages should be 3 |&first_two_messages should be located at the left inside messenger with ${messages_side_margin} margin |&third_message should be located at the right inside messenger with ${messages_side_margin} margin |&messages are placed above each other with ${messages_vertical_margin} margin # ...

Jak widać, przeniosłem marginesy do messages_vertical_margin i messages_side_margin . Zadeklarowałem również minimal margines, który jest zakresem od 0 do 1 piksela.

 # ... @set minimal 0 to 1px = Conversations Panel = | &conversations are aligned above each other with ${minimal} margin # ...

Walidacja oparta na obrazach i wyrażenia niestandardowe

Po omówieniu rozmieszczenia wszystkich głównych elementów na stronie postanowiłem również przetestować stylizację. Chciałem sprawdzić, czy każda wiadomość ma kolor tła dostosowany do roli użytkownika. Gdy użytkownicy są zalogowani, wiadomości będą miały jasnoniebieskie tło. Wiadomości dla innych użytkowników miałyby białe tło. W przypadku, gdy wiadomość nie została wysłana, alert o błędzie miałby różowe tło. Oto zasada, która pomogła mi zweryfikować te style:

 @set OWN_MESSAGE_COLOR #E1E8F5 OTHERS_MESSAGE_COLOR white ERROR_MESSAGE_COLOR #FFE6E6 @rule %{item} should be styled as %{style} message ${item}: @if ${style === "own"} color-scheme > 60% ${OWN_MESSAGE_COLOR}, 0.2 to 20 % ${MAJOR_TEXT_MESSAGE_COLOR} @elseif ${style === "error"} color-scheme > 60% ${ERROR_MESSAGE_COLOR}, 0.2 to 20 % ${MAJOR_TEXT_MESSAGE_COLOR} @else color-scheme > 60% ${OTHERS_MESSAGE_COLOR}, 0.2 to 20 % ${MAJOR_TEXT_MESSAGE_COLOR}

Specyfikacja color-scheme weryfikuje proporcjonalny rozkład kolorów w elemencie. Przycina zrzut ekranu strony i analizuje rozkład kolorów. Tak więc, aby sprawdzić kolor tła elementu, po prostu sprawdzamy, czy jego rozkład jest większy niż 60% całego zakresu kolorów. W teście reguła ta jest wywoływana tak:

 = Styling = |&first_two_messages should be styled as others message |&third_message should be styled as own message

Konfiguracja pakietów testowych

Aplikacja do przesyłania wiadomości to dynamiczna aplikacja, która współpracuje z interfejsem API RESTful Messaging. Dlatego, aby przetestować jego układy we wszystkich różnych stanach, musimy przygotować dane testowe. Zdecydowałem się na makiety Messaging API, aby móc skonfigurować wszystkie moje wiadomości testowe w zestawie testowym. Oto fragment mojego zestawu testów, który pokazuje strukturę naszych testów.

 // ... testOnAllDevices("Unselected 2 conversations", "/", function (driver, device) { mock.onGetMyConversationsReturn(sampleConversations); refresh(driver); new MessageAppPage(driver).waitForIt(); checkLayout(driver, "specs/tests/unselected-conversations.gspec", device.tags); }); testOnAllDevices("When clicking a conversation it should reveal messages", "/", function (driver, device) { mock.onGetMyConversationsReturn(sampleConversations); mock.onGetSingleConversationReturn(sampleMessages); refresh(driver); var page = new MessageAppPage(driver).waitForIt(); page.clickFirstConversation(); checkLayout({ driver: driver, spec: "specs/tests/three-simple-messages-test.gspec", tags: device.tags, vars: { expectedTextProvider: textProvider({ "messenger.message-1": "Hi there!\n11:02", "messenger.message-2": "I want to buy something\n12:02", "messenger.message-3": "Hello! Sure, it's gonna be 100 euros\n13:02" }) } }); }); // ...

Łapanie błędów

Implementacja tych prostych wyrażeń dość szybko się opłaca. Zobaczmy, jakie błędy możemy wykryć za pomocą naszego zestawu testów.

Problem ze stylizacją

Oto przykład, kiedy coś pójdzie nie tak w naszej bazie kodu CSS, w wyniku czego wszystkie wiadomości są renderowane na tym samym tle.

Nieprawidłowy kolor tła wiadomości
Nieprawidłowy kolor tła wiadomości (wyświetl dużą wersję)

Jeśli porównasz ten zrzut ekranu z oryginałem, zauważysz, że ostatnia wiadomość ma białe tło, podczas gdy ma być jasnoniebieskie. Zobaczmy, jak Galen zgłasza ten problem:

Zrzut ekranu z komunikatem o błędzie
Zrzut ekranu z komunikatem o błędzie (wyświetl dużą wersję)

Dla podświetlonego obiektu wyświetla błąd color #e1e8f5 on “messenger.message-3” is 0% but it should be greater than 60% . Szczerze mówiąc, ten komunikat o błędzie nie wygląda aż tak jasno, ale ponieważ to sprawdzenie zostało wygenerowane na podstawie niestandardowej reguły, zawsze możemy wyszukać jego oryginalną nazwę w gałęzi raportu:

Zgłaszanie niepowodzenia testu
Zgłaszanie niepowodzenia testu (wyświetl dużą wersję)

Jeśli przewiniesz w górę, zobaczysz, że oryginalna instrukcja to &third_message should be styled as own message . To kolejna zaleta używania wyrażeń niestandardowych: pomagają zrozumieć niepowodzenie i ładnie opisują wszystkie wygenerowane walidacje.

Problem z pozycjonowaniem

Oto kolejny przykład nieprawidłowego układu z powodu nieprawidłowego wyrównania elementu. Na poniższym zrzucie ekranu widać, że ostatnia wiadomość znajduje się po lewej stronie okna wyświetlania wiadomości, a nie po prawej.

Niewłaściwa pozycja wiadomości
Nieprawidłowa pozycja wiadomości (wyświetl dużą wersję)

Spójrzmy jeszcze raz na zrzut ekranu z komunikatem o błędzie:

Niewłaściwa pozycja wiadomości
Nieprawidłowa pozycja wiadomości (wyświetl dużą wersję)

Zrzut ekranu wyróżnia kontener wiadomości i ostatni element wiadomości. Razem z tym pokazuje następujący komunikat o błędzie: “messenger.message-3” is 285px right which is not in range of 22 to 28px . Dla twórców stron internetowych może nie być jasne, dlaczego po prawej stronie oczekuje się marginesu od 22 do 28 pikseli. Ponownie, musiałbyś poszukać oświadczenia walidacji w gałęzi raportu:

Niewłaściwa pozycja wiadomości
Nieprawidłowa pozycja wiadomości (wyświetl dużą wersję)

Oryginalna instrukcja dla tego sprawdzenia to &third_message should be located at the right inside messenger with ~ 25px margin . To ma o wiele więcej sensu. Co więcej, inni inżynierowie front-endu zrozumieją ten raport z testu, nawet jeśli nie napisali testów.

Wskazówki dotyczące testowania układu

Mając na uwadze wszystkie te różne eksperymenty, postanowiłem sformalizować całą naukę w ogólnych wytycznych dotyczących testowania układu. Oto krótka lista kontrolna czynności, które należy wykonać, aby ułatwić rutynowe testy.

  • Zidentyfikuj wzorce układu w projekcie.
  • Uogólnij oświadczenia weryfikacyjne. Spróbuj skondensować większość walidacji w pojedyncze zdania.
  • Komponentyzuj! Zawsze lepiej przenieść testy powtarzających się elementów do dedykowanych komponentów.
  • Używaj znaczących nazw sekcji, reguł i obiektów.
  • Unikaj pikseli. Spróbuj zastąpić wartości pikseli (dokładne wartości lub zakresy) znaczącymi zmiennymi.
  • Dostosuj kod swojej witryny, aby ułatwić testowanie. Pomoże Ci to ustrukturyzować i utrzymać zarówno kod produkcyjny, jak i testowy.

Kryteria akceptacji na ratunek

Często dostaję pytania typu: „Więc jak szczegółowy powinien być test układu? A co konkretnie powinniśmy testować?” Trudno udzielić ogólnej odpowiedzi. Problem polega na tym, że gdy zasięg testu jest mały, pomijasz błędy. Z drugiej strony, jeśli masz zbyt szczegółowe testy, możesz uzyskać wiele fałszywych trafień, a w przyszłości możesz zgubić się w utrzymaniu testów. Więc jest kompromis. Ale wymyśliłem dla siebie ogólne wytyczne. Jeśli podzielisz pracę na mniejsze historyjki użytkownika, łatwiej będzie ustrukturyzować projekt strony w formie kryteriów akceptacji. W końcu możesz umieścić te kryteria akceptacji w swoim kodzie testowym. Na przykład niektóre z tych instrukcji można zdefiniować w postaci reguł niestandardowych, jak pokazano we wszystkich poprzednich przykładach kodu. Dobrym przykładem kryteriów akceptacji byłoby coś takiego:

  • Wyskakujące okienka powinny być wyśrodkowane na ekranie w pionie i poziomie.
  • Powinny mieć szerokość 400 pikseli.
  • Przyciski powinny być wyrównane poziomo.
  • I tak dalej

Gdy opiszesz projekt prostymi zdaniami, łatwiej będzie je przekształcić w instrukcje wielokrotnego użytku i uporządkować kod testowy.

Wniosek

Jak widać, takie ćwiczenie pomaga ustrukturyzować projekt i odkryć ogólne wzorce układu, które mogą być współużytkowane przez wiele elementów strony. Nawet jeśli strona jest złożona i składa się z wielu elementów, zawsze możesz znaleźć sposób na pogrupowanie ich w wyrażeniach układu. Dzięki takiemu podejściu testowanie układu staje się bardziej narzędziem do programowania opartego na testach, pomagając projektować, wdrażać i dostarczać oprogramowanie w sposób przyrostowy, co jest szczególnie przydatne w zwinnym środowisku.

Zasoby

  • Galen Framework (oficjalna strona internetowa)
  • Galen Framework, GitHub
  • Galen Extras (biblioteka), GitHub
  • Galen Bootstrap, GitHub
  • „Samouczki Galen Framework”, YouTube