Мы с большим успехом использовали селен для высокоуровневого тестирования веб-сайтов (в дополнение к обширным тестам документации python на уровне модулей). Однако сейчас мы используем extjs для многих страниц, и оказалось, что сложно включить тесты Selenium для сложных компонентов, таких как сетки.
Кто-нибудь имел успех в написании автоматических тестов для веб-страниц на основе extjs? Многие поисковые запросы находят людей с похожими проблемами, но мало ответов. Благодарность!
unit-testing
extjs
selenium
web-testing
2001 мм
источник
источник
Ответы:
Самым большим препятствием при тестировании ExtJS с Selenium является то, что ExtJS не отображает стандартные элементы HTML, а Selenium IDE наивно (и справедливо) генерирует команды, нацеленные на элементы, которые просто действуют как декор - лишние элементы, которые помогают ExtJS со всем рабочим столом. Смотри и чувствуй. Вот несколько советов и приемов, которые я собрал при написании автоматического теста Selenium для приложения ExtJS.
Общие советы
Расположение элементов
При создании тестовых примеров Selenium путем записи действий пользователя с помощью Selenium IDE в Firefox, Selenium будет основывать записанные действия на идентификаторах элементов HTML. Однако для большинства интерактивных элементов ExtJS использует сгенерированные идентификаторы, такие как «ext-gen-345», которые могут измениться при последующем посещении той же страницы, даже если не было внесено никаких изменений в код. После записи действий пользователя для теста необходимо вручную выполнить все действия, которые зависят от сгенерированных идентификаторов, и заменить их. Возможны два типа замен:
Замена локатора идентификатора на локатор CSS или XPath
Локаторы CSS начинаются с «css =», а локаторы XPath начинаются с «//» (префикс «xpath =» необязателен). Локаторы CSS менее подробны, их легче читать, и им следует отдавать предпочтение перед локаторами XPath. Однако могут быть случаи, когда необходимо использовать локаторы XPath, потому что локатор CSS просто не может его вырезать.
Выполнение JavaScript
Некоторым элементам требуется больше, чем простое взаимодействие с мышью и клавиатурой из-за сложной визуализации, выполняемой ExtJS. Например, Ext.form.CombBox на самом деле не
<select>
элемент, а текстовый ввод с отдельным раскрывающимся списком, который находится где-то в нижней части дерева документа. Чтобы правильно имитировать выбор ComboBox, можно сначала смоделировать щелчок по стрелке раскрывающегося списка, а затем щелкнуть по появившемуся списку. Однако поиск этих элементов с помощью локаторов CSS или XPath может быть обременительным. Альтернативный вариант - найти сам компонент ComoBox и вызвать на нем методы для имитации выбора: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
В Selenium
runScript
команду можно использовать для выполнения вышеуказанной операции в более краткой форме:with (Ext.getCmp('genderComboBox')) { setValue('female'); fireEvent('select'); }
Как справиться с AJAX и медленным рендерингом
Selenium имеет разновидности «* AndWait» для всех команд ожидания загрузки страницы, когда действие пользователя приводит к переходу или перезагрузке страницы. Однако, поскольку выборка AJAX не включает фактическую загрузку страницы, эти команды нельзя использовать для синхронизации. Решение состоит в том, чтобы использовать визуальные подсказки, такие как наличие / отсутствие индикатора прогресса AJAX или появление строк в сетке, дополнительных компонентов, ссылок и т. Д. Например:
Command: waitForElementNotPresent Target: css=div:contains('Loading...')
Иногда элемент появляется только через определенное время, в зависимости от того, насколько быстро ExtJS отрисовывает компоненты после того, как действие пользователя приводит к изменению представления. Вместо использования произвольных задержек с
pause
командой, идеальный метод - подождать, пока интересующий элемент не окажется в пределах нашей досягаемости. Например, чтобы щелкнуть элемент после того, как он появится:Command: waitForElementPresent Target: css=span:contains('Do the funky thing') Command: click Target: css=span:contains('Do the funky thing')
Использование произвольных пауз - не лучшая идея, поскольку различия во времени, возникающие в результате выполнения тестов в разных браузерах или на разных машинах, сделают тестовые примеры нестабильными.
Не кликабельные элементы
Некоторые элементы не могут быть активированы
click
командой. Это потому, что прослушиватель событий фактически находится в контейнере, наблюдая за событиями мыши на его дочерних элементах, которые в конечном итоге всплывают до родительского. Вкладка - один из примеров. Чтобы щелкнуть вкладку a, вы должны смоделироватьmouseDown
событие на метке вкладки:Command: mouseDownAt Target: css=.x-tab-strip-text:contains('Options') Value: 0,0
Проверка поля
Поля формы (компоненты Ext.form. *), Которые имеют связанные регулярные выражения или vtypes для проверки, будут запускать проверку с определенной задержкой (см.
validationDelay
Свойство, которое по умолчанию установлено на 250 мс), после того, как пользователь вводит текст или сразу после того, как поле теряет фокус - или размывает (см.validateOnDelay
свойство). Чтобы запустить проверку поля после выдачи команды типа Selenium для ввода текста внутри поля, вам необходимо выполнить одно из следующих действий:Запуск отложенной проверки
ExtJS запускает таймер задержки проверки, когда поле получает события нажатия клавиш. Чтобы запустить этот таймер, просто вызовите фиктивное событие keyup (не имеет значения, какой ключ вы используете, поскольку ExtJS игнорирует его), за которым следует короткая пауза, которая длиннее, чем validationDelay:
Command: keyUp Target: someTextArea Value: x Command: pause Target: 500
Запуск немедленной проверки
Вы можете ввести в поле событие размытия, чтобы вызвать немедленную проверку:
Command: runScript Target: someComponent.nameTextField.fireEvent("blur")
Проверка результатов проверки
После проверки вы можете проверить наличие или отсутствие поля ошибки:
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"))]
Обратите внимание, что проверка «display: none» необходима, потому что после отображения поля ошибки, а затем его необходимо скрыть, ExtJS просто скроет поле ошибки, а не полностью удалит его из дерева DOM.
Советы по отдельным элементам
Щелчок по кнопке Ext.form.Button
Опция 1
Команда: нажмите Цель: css = button: contains ('Сохранить')
Выбирает кнопку по ее подписи
Вариант 2
Команда: нажмите кнопку Target: css = # save-options
Выбирает кнопку по ее id
Выбор значения из Ext.form.ComboBox
Command: runScript Target: with (Ext.getCmp('genderComboBox')) { setValue('female'); fireEvent('select'); }
Сначала устанавливает значение, а затем явно запускает событие выбора, если есть наблюдатели.
источник
Этот блог мне очень помог. Он довольно много писал по этой теме, и, похоже, она все еще активна. Кажется, парень тоже ценит хороший дизайн.
Он в основном говорит об использовании отправки javascript для выполнения запросов и использовании метода Ext.ComponentQuery.query для извлечения данных так же, как вы это делаете в своем приложении ext внутри. Таким образом, вы можете использовать xtypes и itemIds и не беспокоиться о попытках разобрать любой из безумных автоматически сгенерированных материалов.
Я нашел эту статью особенно очень полезной.
Возможно, в ближайшее время я опубликую здесь что-то более подробное - все еще пытаюсь понять, как это сделать правильно
источник
Я тестировал свое веб-приложение ExtJs на селене. Одна из самых больших проблем заключалась в выборе элемента в сетке, чтобы что-то с ним сделать.
Для этого я написал вспомогательный метод (в классе SeleniumExtJsUtils, который представляет собой набор полезных методов для более простого взаимодействия с ExtJ):
/** * 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)"; }
и когда мне нужно было выбрать строку, я просто звонил:
selenium.runScript( SeleniumExtJsUtils.selectGridRow("<myGridId>", 5) );
Чтобы это сработало, мне нужно установить свой идентификатор в сетке и не позволять ExtJ генерировать его собственный.
источник
Чтобы обнаружить, что этот элемент видим, вы используете предложение:
not(contains(@style, "display: none")
Лучше использовать это:
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')]"
источник
Можете ли вы подробнее рассказать о типах проблем, с которыми вы сталкиваетесь при тестировании extjs?
Одно расширение Selenium, которое я считаю полезным, - это waitForCondition . Если ваша проблема связана с событиями Ajax, вы можете использовать waitForCondition, чтобы дождаться возникновения событий.
источник
Веб-страницы Ext JS может быть сложно протестировать из-за сложного HTML, который они генерируют, как в случае с сетками Ext JS.
HTML5 Robot решает эту проблему, используя ряд передовых методов для надежного поиска и взаимодействия с компонентами на основе атрибутов и условий, которые не являются динамическими. Затем он предоставляет ярлыки для этого со всеми компонентами HTML, Ext JS и Sencha Touch, с которыми вам необходимо взаимодействовать. Он бывает двух видов:
Например, если вы хотите найти строку сетки Ext JS, содержащую текст «Foo», вы можете сделать в Java следующее:
findExtJsGridRow("Foo");
... и в Гвен вы могли бы делать следующее:
extjsgridrow by text "Foo"
Как для Java, так и для Gwen существует множество документации по работе со специфическими компонентами Ext JS. В документации также подробно описывается итоговый HTML-код для всех этих компонентов Ext JS, который также может оказаться полезным.
источник
Полезные советы по извлечению сетки через идентификатор сетки на странице: я думаю, вы можете расширить более полезную функцию из этого 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; }
источник
Упрощенное тестирование с помощью пользовательских атрибутов данных HTML
Из документации Sencha :
Переопределив метод
Ext.AbstractComponent
'sonBoxReady
, я устанавливаю пользовательский атрибут данных (имя которого происходит от моего настраиваемогоtestIdAttr
свойства каждого компонента) на значение компонентаitemId
, если оно существует. ДобавьтеTesting.overrides.AbstractComponent
класс в массив вашегоapplication.js
файлаrequires
./** * 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); } });
Этот метод предоставляет разработчикам возможность повторно использовать описательный идентификатор в своем коде и иметь доступ к этим идентификаторам при каждом отображении страницы. Больше никакого поиска по не описательным, динамически генерируемым идентификаторам.
источник
Мы разрабатываем среду тестирования, которая использует селен и столкнулась с проблемами с extjs (поскольку это рендеринг на стороне клиента). Я считаю полезным поискать элемент, когда DOM будет готов.
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"); }
источник
Для сложного пользовательского интерфейса, который не является формальным HTML, вы всегда можете рассчитывать на xPath, но это немного сложно, когда дело доходит до другой реализации пользовательского интерфейса с использованием ExtJ.
Вы можете использовать Firebug и Firexpath в качестве расширений firefox для проверки xpath определенного элемента и просто передать полный xpath в качестве параметра для selenium.
Например, в java-коде:
String fullXpath = "xpath=//div[@id='mainDiv']//div[contains(@class,'x-grid-row')]//table/tbody/tr[1]/td[1]//button" selenium.click(fullXpath);
источник
Когда я тестировал приложение ExtJS с помощью WebDriver, я использовал следующий подход: я искал поле по тексту метки и получал
@for
атрибут из метки. Например, у нас есть этикетка<label id="dynamic_id_label" class="TextboxLabel" for="textField_which_I_am_lloking_for"> Name Of Needed Label <label/>
И нам нужно указать WebDriver некоторых входного сигнала:
//input[@id=(//label[contains(text(),'Name Of Needed Label')]/@for)]
.Итак, он выберет идентификатор из
@for
атрибута и будет использовать его в дальнейшем. Это, вероятно, самый простой случай, но он дает вам возможность найти элемент. Это намного сложнее, когда у вас нет метки, но тогда вам нужно найти какой-то элемент и написать свой xpath для поиска братьев и сестер, элементов по убыванию / возрастанию.источник