Вопрос по extjs, unit-testing, selenium, web-testing – Любые предложения для тестирования кода extjs в браузере, предпочтительно с селеном?

90

Мы с большим успехом использовали селен для проведения высокоуровневого тестирования веб-сайтов (в дополнение к обширным тестам по питону на уровне модулей). Однако теперь мы используем extjs для большого количества страниц, и оказалось затруднительным включить тесты Selenium для сложных компонентов, таких как сетки.

Кто-нибудь имел успех в написании автоматизированных тестов для веб-страниц на основе extjs? Много гуглят находит людей с похожими проблемами, но мало ответов. Спасибо!

Хорошие люди из Ext JS были достаточно любезны, чтобы опубликовать эту тему в своем блоге.here, Надеюсь, это поможет. NBRed5

Ваш Ответ

11   ответов
2

которая использует селен и столкнулась с проблемами с 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");
}
3

HTML, который они в конечном итоге генерируют как с сетками Ext JS.

HTML5 робот решает эту проблему, используя ряд передовых методик для надежного поиска и взаимодействия с компонентами на основе атрибутов и условий, которые не являются динамическими. Затем он предоставляет ярлыки для выполнения этого со всеми компонентами HTML, Ext JS и Sencha Touch, с которыми вам нужно будет взаимодействовать. Он поставляется в 2 вариантах:

Java - Familiar Selenium and JUnit based API that has built in web driver support for all modern browsers. Gwen - A human style language for quickly and easily creating and maintaining browser tests, which comes with its own integrated development environment. All of which is based on the Java API.

Например, если вы хотите найти строку сетки Ext JS, содержащую текст & quot; Foo & quot ;, вы можете сделать следующее в Java:

findExtJsGridRow("Foo");

... и вы могли бы сделать следующее в Гвен:

extjsgridrow by text "Foo"

Существует много документации для обоихДжава и Гвен за то, как работать со специфическими компонентами Ext JS. В документации также подробно описывается итоговый HTML-код для всех этих компонентов Ext JS, что также может оказаться полезным.

1

который не является формальным 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);
2

е: Я думаю, что вы можете расширить более полезную функцию из этого 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;
    }
3

с которыми вы сталкиваетесь при тестировании extjs?

Одно расширение Selenium, которое я считаю полезнымwaitForCondition, Если ваша проблема связана с событиями Ajax, вы можете использовать waitForCondition для ожидания событий.

5

блог очень помог мне Он много писал на эту тему, и кажется, что он все еще активен. Парень также, кажется, ценит хороший дизайн.

Он в основном говорит об использовании отправки javascript для выполнения запросов и об использовании метода Ext.ComponentQuery.query для извлечения материала так же, как вы делаете внутри вашего ext приложения. Таким образом, вы можете использовать xtypes и itemIds, и вам не придется беспокоиться о попытке разобрать любой из сумасшедших автоматически сгенерированных вещей.

я нашелЭта статья в частности очень полезно.

Возможно, скоро выложу кое-что более подробное здесь - все еще пытаясь понять, как сделать правильно

4

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')]"
2
Easier testing through custom HTML data- attributes

Сенча документация:

An itemId can be used as an alternative way to get a reference to a component when no object reference is available. Instead of using an id with Ext.getCmp, use itemId with Ext.container.Container.getComponent which will retrieve itemId's or id's. Since itemId's are an index to the container's internal MixedCollection, the itemId is scoped locally to the container -- avoiding potential conflicts with Ext.ComponentManager which requires a unique id.

ПереопределениеExt.AbstractComponent& APOS; sonBoxReady Метод, я устанавливаю пользовательский атрибут данных (имя которого происходит от моего пользовательскогоtestIdAttr свойство каждого компонента) к компонентуitemId значение, если оно существует. ДобавитьTesting.overrides.AbstractComponent класс к вашемуapplication.js Файл & APOS; srequires массив.

/**
 * 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);
  }
});

Этот метод предоставляет разработчикам способ повторно использовать описательный идентификатор в их коде и получать эти идентификаторы каждый раз при визуализации страницы. Больше не нужно искать через неописательные, динамически генерируемые идентификаторы.

4

их проблем был выбор элемента в сетке, чтобы что-то с ним сделать.

Для этого я написал вспомогательный метод (в классе SeleniumExtJsUtils, который представляет собой набор полезных методов для более легкого взаимодействия с 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)";
}

и когда мне нужно было выбрать строку, я просто позвонил:

selenium.runScript( SeleniumExtJsUtils.selectGridRow("<myGridId>", 5) );

Чтобы это работало, мне нужно установить свой идентификатор в сетке и не позволять ExtJ создавать его самостоятельно.

вы должны быть в состоянии найти сетку, используя ее xtype в Ext.ComponentQuery.query (при условии, что вы расширились от grid и определили xtype для вашей собственной сетки). Я также обнаружил, что вы можете нажать на материал, используя xpath, чтобы найти тд, который содержит идентифицирующее значение для строки
1

я использовал следующий подход: я искал поле по тексту метки и получил@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 в поисках элементов типа «братья и сестры», спуск / восход.

173

что ExtJS не рендерит стандартные элементы HTML, а Selenium IDE будет наивно (и по праву) генерировать команды, нацеленные на элементы, которые просто действуют как декорации - лишние элементы, которые помогают ExtJS с целым рабочим столом. Смотри и чувствуй. Вот несколько советов и приемов, которые я собрал, когда писал автоматический тест Selenium для приложения ExtJS.

General Tips Locating Elements

При создании тестовых примеров Selenium путем записи действий пользователя с помощью Selenium IDE в Firefox Selenium будет основывать записанные действия на идентификаторах элементов HTML. Однако для большинства интерактивных элементов ExtJS использует сгенерированные идентификаторы, подобные & quot; ext-gen-345 & quot; которые могут измениться при последующем посещении той же страницы, даже если не было никаких изменений кода. После записи действий пользователя для теста необходимо вручную выполнить все такие действия, которые зависят от сгенерированных идентификаторов, и заменить их. Существует два типа замен:

Replacing an Id Locator with a CSS or XPath Locator

CSS-локаторы начинаются с & quot; css = & quot; и локаторы XPath начинаются с & quot; // & quot; (префикс «xpath =» является необязательным). CSS-локаторы менее многословны, их легче читать, и им следует отдавать предпочтение перед локаторами XPath. Однако могут быть случаи, когда необходимо использовать локаторы XPath, потому что локатор CSS просто не может его обрезать.

Executing 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

В СеленеrunScript Команда может использоваться для выполнения вышеуказанной операции в более сжатой форме:

with (Ext.getCmp('genderComboBox')) { setValue('female'); fireEvent('select'); }
Coping with AJAX and Slow Rendering

Селен имеет "* 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')

Полагаться на произвольные паузы не очень хорошая идея, поскольку различия во времени, возникающие в результате выполнения тестов в разных браузерах или на разных машинах, делают тестовые случаи нестабильными.

Non-clickable Items

Некоторые элементы не могут быть вызваныclick команда. Это потому, что прослушиватель событий фактически находится в контейнере, наблюдая за событиями мыши на его дочерних элементах, которые в конечном итоге всплывают до родительского элемента. Элемент управления вкладки является одним из примеров. Чтобы нажать на вкладку, вы должны смоделироватьmouseDown событие на вкладке ярлык:

Command: mouseDownAt
Target: css=.x-tab-strip-text:contains('Options')
Value: 0,0
Field Validation

Поля формы (компоненты Ext.form. *), Связанные с регулярными выражениями или vtypes для проверки, будут запускать проверку с определенной задержкой (см.validationDelay свойство, которое установлено на 250 мс по умолчанию), после того, как пользователь вводит текст или сразу, когда поле теряет фокус - или размывает (см.validateOnDelay имущество). Чтобы запустить проверку поля после ввода команды типа Selenium для ввода некоторого текста в поле, необходимо выполнить одно из следующих действий:

Triggering Delayed Validation

ExtJS fires off the validation delay timer when the field receives keyup events. To trigger this timer, simply issue a dummy keyup event (it doesn't matter which key you use as ExtJS ignores it), followed by a short pause that is longer than the validationDelay:

Command: keyUp
Target: someTextArea
Value: x
Command: pause
Target: 500

Triggering Immediate Validation

You can inject a blur event into the field to trigger immediate validation:

Command: runScript
Target: someComponent.nameTextField.fireEvent("blur")
Checking for Validation Results

После проверки вы можете проверить наличие или отсутствие поля ошибки:

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"))]

Обратите внимание, что & quot; отображение: нет & quot; проверка необходима, потому что, как только отображается поле ошибки, а затем его нужно скрыть, ExtJS просто скрывает поле ошибки, а не полностью удаляет его из дерева DOM.

Element-specific Tips Clicking an Ext.form.Button

Option 1

Command: click Target: css=button:contains('Save')

Selects the button by its caption

Option 2

Command: click Target: css=#save-options button

Selects the button by its id

Selecting a Value from an Ext.form.ComboBox
Command: runScript
Target: with (Ext.getCmp('genderComboBox')) { setValue('female'); fireEvent('select'); }

Сначала устанавливается значение, а затем явно запускается событие выбора в случае, если есть наблюдатели.

Вот несколько советов по эксплуатации:rkapse.blogspot.nl/2009/06/selenium-ide-issues-with-extjs.html

Похожие вопросы