Z dużym powodzeniem używamy selenu do przeprowadzania testów stron internetowych na wysokim poziomie (oprócz rozbudowanych testów doctonów w języku Python na poziomie modułu). Jednak teraz używamy extjów do wielu stron i okazuje się, że trudno jest włączyć testy Selenium do złożonych komponentów, takich jak siatki.
Czy ktoś odniósł sukces w pisaniu automatycznych testów dla stron internetowych opartych na extjs? Wiele wyszukiwań w Google znajduje ludzi z podobnymi problemami, ale niewiele odpowiedzi. Dzięki!
unit-testing
extjs
selenium
web-testing
mm2001
źródło
źródło
Odpowiedzi:
Największą przeszkodą w testowaniu ExtJS z Selenium jest to, że ExtJS nie renderuje standardowych elementów HTML, a Selenium IDE naiwnie (i słusznie) generuje polecenia skierowane do elementów, które działają tylko jako dekoracje - zbędne elementy, które pomagają ExtJS z całym pulpitem- widzieć i czuć. Oto kilka wskazówek i sztuczek, które zebrałem podczas pisania automatycznego testu Selenium dla aplikacji ExtJS.
Ogólne wskazówki
Lokalizowanie elementów
Podczas generowania przypadków testowych Selenium poprzez rejestrowanie działań użytkownika za pomocą Selenium IDE w przeglądarce Firefox, Selenium oprze zarejestrowane działania na identyfikatorach elementów HTML. Jednak w przypadku większości klikalnych elementów ExtJS używa wygenerowanych identyfikatorów, takich jak „ext-gen-345”, które prawdopodobnie zmienią się podczas kolejnej wizyty na tej samej stronie, nawet jeśli nie wprowadzono żadnych zmian w kodzie. Po zarejestrowaniu działań użytkownika na potrzeby testu należy wykonać ręcznie wszystkie czynności, które zależą od wygenerowanych identyfikatorów, i je zastąpić. Istnieją dwa rodzaje zamienników, które można wykonać:
Zastąpienie lokalizatora identyfikatora lokalizatorem CSS lub XPath
Lokalizatory CSS zaczynają się od „css =”, a lokalizatory XPath zaczynają się od „//” (przedrostek „xpath =” jest opcjonalny). Lokalizatory CSS są mniej szczegółowe i łatwiejsze do odczytania i powinny być preferowane względem lokalizatorów XPath. Jednak mogą wystąpić przypadki, w których trzeba użyć lokalizatorów XPath, ponieważ lokalizator CSS po prostu nie może ich wyciąć.
Wykonywanie JavaScript
Niektóre elementy wymagają czegoś więcej niż tylko prostych interakcji myszy / klawiatury ze względu na złożone renderowanie wykonywane przez ExtJS. Na przykład Ext.form.CombBox nie jest w rzeczywistości
<select>
elementem, ale tekstem z odłączoną listą rozwijaną, która znajduje się gdzieś na dole drzewa dokumentu. Aby poprawnie zasymulować wybór ComboBox, można najpierw zasymulować kliknięcie strzałki rozwijanej, a następnie kliknięcie wyświetlonej listy. Jednak lokalizowanie tych elementów za pomocą lokalizatorów CSS lub XPath może być kłopotliwe. Alternatywą jest zlokalizowanie samego komponentu ComoBox i wywołanie na nim metod w celu zasymulowania wyboru:var combo = Ext.getCmp('genderComboBox'); // returns the ComboBox components combo.setValue('female'); // set the value combo.fireEvent('select'); // because setValue() doesn't trigger the event
W Selenium
runScript
polecenie może być użyte do wykonania powyższej operacji w bardziej zwięzłej formie:with (Ext.getCmp('genderComboBox')) { setValue('female'); fireEvent('select'); }
Radzenie sobie z AJAX i Slow Rendering
Selenium ma smaki "* AndWait" dla wszystkich poleceń czekających na załadowanie strony, gdy akcja użytkownika skutkuje przejściem strony lub ponownym załadowaniem. Ponieważ jednak pobieranie AJAX nie obejmuje faktycznego ładowania strony, tych poleceń nie można używać do synchronizacji. Rozwiązaniem jest wykorzystanie wizualnych wskazówek, takich jak obecność / brak wskaźnika postępu AJAX lub wygląd wierszy w siatce, dodatkowe komponenty, linki itp. Na przykład:
Command: waitForElementNotPresent Target: css=div:contains('Loading...')
Czasami element pojawia się dopiero po pewnym czasie, w zależności od tego, jak szybko ExtJS renderuje komponenty po tym, jak akcja użytkownika powoduje zmianę widoku. Zamiast używać arbitralnych opóźnień przy
pause
poleceniu, idealną metodą jest zaczekanie, aż interesujący nas element znajdzie się w naszym zasięgu. Na przykład, aby kliknąć element po odczekaniu, aż się pojawi:Command: waitForElementPresent Target: css=span:contains('Do the funky thing') Command: click Target: css=span:contains('Do the funky thing')
Poleganie na arbitralnych przerwach nie jest dobrym pomysłem, ponieważ różnice czasowe wynikające z uruchamiania testów w różnych przeglądarkach lub na różnych komputerach spowodują, że przypadki testowe będą niestabilne.
Elementy nieklikalne
Niektórych elementów nie można wywołać
click
poleceniem. Dzieje się tak, ponieważ detektor zdarzeń znajduje się w kontenerze, obserwując zdarzenia myszy w elementach podrzędnych, które ostatecznie pojawiają się w nadrzędnym. Kontrolka karty jest jednym z przykładów. Aby kliknąć zakładkę, musisz zasymulowaćmouseDown
zdarzenie na etykiecie zakładki:Command: mouseDownAt Target: css=.x-tab-strip-text:contains('Options') Value: 0,0
Walidacja pola
Pola formularza (komponenty Ext.form. *), Które mają skojarzone wyrażenia regularne lub vtypes do walidacji, wyzwalają walidację z pewnym opóźnieniem (patrz
validationDelay
właściwość, która jest domyślnie ustawiona na 250 ms), po wprowadzeniu tekstu przez użytkownika lub natychmiast po utracie pola focus - lub rozmywa (zobaczvalidateOnDelay
właściwość). Aby wyzwolić walidację pola po wydaniu polecenia typu Selenium w celu wprowadzenia tekstu w polu, należy wykonać jedną z następujących czynności:Wyzwalanie opóźnionej walidacji
ExtJS odpala licznik czasu opóźnienia walidacji, gdy pole otrzymuje zdarzenia keyup. Aby uruchomić ten licznik czasu, po prostu wydaj fałszywe zdarzenie klucza (nie ma znaczenia, którego klucza używasz, ponieważ ExtJS go ignoruje), a następnie krótką pauzę dłuższą niż validationDelay:
Command: keyUp Target: someTextArea Value: x Command: pause Target: 500
Wyzwalanie natychmiastowej weryfikacji
Możesz wstrzyknąć zdarzenie rozmycia w pole, aby uruchomić natychmiastową weryfikację:
Command: runScript Target: someComponent.nameTextField.fireEvent("blur")
Sprawdzanie wyników walidacji
Po sprawdzeniu poprawności możesz sprawdzić obecność lub brak pola błędu:
Command: verifyElementNotPresent Target: //*[@id="nameTextField"]/../*[@class="x-form-invalid-msg" and not(contains(@style, "display: none"))] Command: verifyElementPresent Target: //*[@id="nameTextField"]/../*[@class="x-form-invalid-msg" and not(contains(@style, "display: none"))]
Zwróć uwagę, że sprawdzenie "display: none" jest konieczne, ponieważ po wyświetleniu pola błędu, a następnie trzeba je ukryć, ExtJS po prostu ukryje pole błędu zamiast całkowicie usunąć je z drzewa DOM.
Wskazówki dotyczące poszczególnych elementów
Kliknięcie Ext.form.Button
opcja 1
Polecenie: kliknij Cel: przycisk css =: zawiera ('Zapisz')
Zaznacza przycisk według jego podpisu
Opcja 2
Polecenie: kliknij przycisk Cel: css = # opcje zapisywania
Wybiera przycisk według jego identyfikatora
Wybieranie wartości z Ext.form.ComboBox
Command: runScript Target: with (Ext.getCmp('genderComboBox')) { setValue('female'); fireEvent('select'); }
Najpierw ustawia wartość, a następnie jawnie uruchamia wybrane zdarzenie w przypadku obecności obserwatorów.
źródło
Ten blog bardzo mi pomógł. Napisał sporo na ten temat i wygląda na to, że nadal jest aktywny. Facet wydaje się też doceniać dobry projekt.
Mówi po prostu o używaniu wysyłania javascript do wykonywania zapytań i używaniu metody Ext.ComponentQuery.query do pobierania rzeczy w taki sam sposób, jak robisz to w wewnętrznej aplikacji ext. W ten sposób możesz używać xtypes i itemIds i nie musisz się martwić o próbę parsowania jakichkolwiek szalonych automatycznie generowanych rzeczy.
Znalazłem ten artykuł w szczególności bardzo pomocne.
Może wkrótce opublikuję tutaj coś bardziej szczegółowego - wciąż próbuję dowiedzieć się, jak to zrobić poprawnie
źródło
Testowałem moją aplikację internetową ExtJs z selenem. Jednym z największych problemów było wybranie elementu w siatce, aby coś z nim zrobić.
W tym celu napisałem metodę pomocniczą (w klasie SeleniumExtJsUtils będącej zbiorem przydatnych metod ułatwiających interakcję z ExtJs):
/** * Javascript needed to execute in order to select row in the grid * * @param gridId Grid id * @param rowIndex Index of the row to select * @return Javascript to select row */ public static String selectGridRow(String gridId, int rowIndex) { return "Ext.getCmp('" + gridId + "').getSelectionModel().selectRow(" + rowIndex + ", true)"; }
a kiedy chciałem wybrać wiersz, dzwoniłem po prostu:
selenium.runScript( SeleniumExtJsUtils.selectGridRow("<myGridId>", 5) );
Aby to zadziałało, muszę ustawić mój identyfikator w sieci i nie pozwolić ExtJ generować własnego.
źródło
Aby wykryć, że element jest widoczny, użyj klauzuli:
not(contains(@style, "display: none")
Lepiej użyć tego:
visible_clause = "not(ancestor::*[contains(@style,'display: none')" + " or contains(@style, 'visibility: hidden') " + " or contains(@class,'x-hide-display')])" hidden_clause = "parent::*[contains(@style,'display: none')" + " or contains(@style, 'visibility: hidden')" + " or contains(@class,'x-hide-display')]"
źródło
Czy możesz podać więcej informacji na temat typów problemów, które masz podczas testowania extjs?
Jednym z przydatnych rozszerzeń Selenium jest waitForCondition . Jeśli wydaje się, że problem dotyczy zdarzeń Ajax, możesz użyć waitForCondition, aby czekać na wystąpienie zdarzeń.
źródło
Strony internetowe Ext JS mogą być trudne do przetestowania ze względu na skomplikowany HTML, który generują, podobnie jak w przypadku siatek Ext JS.
HTML5 Robot radzi sobie z tym, stosując serię najlepszych praktyk dotyczących niezawodnego wyszukiwania i interakcji z komponentami w oparciu o atrybuty i warunki, które nie są dynamiczne. Następnie udostępnia skróty do zrobienia tego ze wszystkimi komponentami HTML, Ext JS i Sencha Touch, z którymi musiałbyś korzystać. Występuje w 2 smakach:
Na przykład, jeśli chcesz znaleźć wiersz siatki Ext JS zawierający tekst „Foo”, możesz wykonać następujące czynności w Javie:
findExtJsGridRow("Foo");
... i możesz wykonać następujące czynności w Gwen:
extjsgridrow by text "Foo"
Istnieje wiele dokumentacji zarówno dla Javy, jak i Gwen, aby dowiedzieć się, jak pracować z określonymi komponentami Ext JS. Dokumentacja zawiera również szczegóły dotyczące wynikowego kodu HTML dla wszystkich tych komponentów Ext JS, co również może okazać się przydatne.
źródło
Przydatne wskazówki dotyczące pobierania siatki za pomocą identyfikatora siatki na stronie: Myślę, że możesz rozszerzyć bardziej użyteczną funkcję z tego interfejsu API.
sub get_grid_row { my ($browser, $grid, $row) = @_; my $script = "var doc = this.browserbot.getCurrentWindow().document;\n" . "var grid = doc.getElementById('$grid');\n" . "var table = grid.getElementsByTagName('table');\n" . "var result = '';\n" . "var row = 0;\n" . "for (var i = 0; i < table.length; i++) {\n" . " if (table[i].className == 'x-grid3-row-table') {\n". " row++;\n" . " if (row == $row) {\n" . " var cols_len = table[i].rows[0].cells.length;\n" . " for (var j = 0; j < cols_len; j++) {\n" . " var cell = table[i].rows[0].cells[j];\n" . " if (result.length == 0) {\n" . " result = getText(cell);\n" . " } else { \n" . " result += '|' + getText(cell);\n" . " }\n" . " }\n" . " }\n" . " }\n" . "}\n" . "result;\n"; my $result = $browser->get_eval($script); my @res = split('\|', $result); return @res; }
źródło
Łatwiejsze testowanie dzięki niestandardowym atrybutom danych HTML
Z dokumentacji Sencha :
Zastępując metodę
Ext.AbstractComponent
'sonBoxReady
, ustawiłem niestandardowy atrybut danych (którego nazwa pochodzi z mojej niestandardowejtestIdAttr
właściwości każdego komponentu) na wartość komponentuitemId
, jeśli istnieje. DodajTesting.overrides.AbstractComponent
klasę do tablicyapplication.js
plikurequires
./** * Overrides the Ext.AbstracComponent's onBoxReady * method to add custom data attributes to the * component's dom structure. * * @author Brian Wendt */ Ext.define('Testing.overrides.AbstractComponent', { override: 'Ext.AbstractComponent', onBoxReady: function () { var me = this, el = me.getEl(); if (el && el.dom && me.itemId) { el.dom.setAttribute(me.testIdAttr || 'data-selenium-id', me.itemId); } me.callOverridden(arguments); } });
Ta metoda umożliwia programistom ponowne użycie identyfikatora opisowego w kodzie i udostępnienie tych identyfikatorów za każdym razem, gdy strona jest renderowana. Koniec z przeszukiwaniem nieopisowych, dynamicznie generowanych identyfikatorów.
źródło
Opracowujemy framework testowy, który używa selenu i napotkał problemy z extjs (ponieważ jest to renderowanie po stronie klienta). Uważam, że warto poszukać elementu, gdy DOM jest gotowy.
public static boolean waitUntilDOMIsReady(WebDriver driver) { def maxSeconds = DEFAULT_WAIT_SECONDS * 10 for (count in 1..maxSeconds) { Thread.sleep(100) def ready = isDOMReady(driver); if (ready) { break; } } } public static boolean isDOMReady(WebDriver driver){ return driver.executeScript("return document.readyState"); }
źródło
W przypadku złożonego interfejsu użytkownika, który nie jest formalnym kodem HTML, xPath jest zawsze czymś, na co można liczyć, ale jest nieco skomplikowany, jeśli chodzi o różne implementacje interfejsu użytkownika za pomocą ExtJ.
Możesz użyć Firebug i Firexpath jako rozszerzeń firefoxa do testowania xpath określonego elementu i po prostu przekazać pełną xpath jako parametr selenu.
Na przykład w kodzie java:
String fullXpath = "xpath=//div[@id='mainDiv']//div[contains(@class,'x-grid-row')]//table/tbody/tr[1]/td[1]//button" selenium.click(fullXpath);
źródło
Kiedy testowałem aplikację ExtJS przy użyciu WebDriver, zastosowałem następne podejście: szukałem pola po tekście etykiety i otrzymałem
@for
atrybut z etykiety. Na przykład mamy etykietę<label id="dynamic_id_label" class="TextboxLabel" for="textField_which_I_am_lloking_for"> Name Of Needed Label <label/>
I musimy wskazać WebDriver jakiś wkład:
//input[@id=(//label[contains(text(),'Name Of Needed Label')]/@for)]
.Więc wybierze identyfikator z
@for
atrybutu i użyje go dalej. Jest to prawdopodobnie najprostszy przypadek, ale pozwala zlokalizować element. Jest to znacznie trudniejsze, gdy nie masz etykiety, ale wtedy musisz znaleźć jakiś element i napisać swoją ścieżkę XP w poszukiwaniu rodzeństwa, elementów descend / ascend.źródło