Есть какие-нибудь предложения по тестированию кода extjs в браузере, желательно с селеном?

StackOverflow https://stackoverflow.com/questions/107314

Вопрос

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

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

Это было полезно?

Решение

Самым большим препятствием при тестировании 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

В Селене 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 команда.Это потому, что прослушиватель событий на самом деле находится в контейнере и отслеживает события мыши на его дочерних элементах, которые в конечном итоге передаются родительскому элементу.Элемент управления вкладками является одним из примеров.Чтобы щелкнуть вкладку, вам нужно смоделировать mouseDown событие на метке вкладки:

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

Проверка поля

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

  • Запуск отложенной проверки

    ExtJS запускает таймер задержки проверки, когда поле получает события нажатия клавиш.Чтобы запустить этот таймер, просто вызовите фиктивное событие нажатия клавиши (неважно, какой ключ вы используете, поскольку 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"))]

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

Советы по конкретным элементам

Нажатие кнопки Ext.form.Button

  • Опция 1

    Команда:Нажмите на цель:css=button:contains('Сохранить')

    Выбирает кнопку по ее заголовку

  • Вариант 2

    Команда:Нажмите на цель:css=#кнопка параметров сохранения

    Выбирает кнопку по ее идентификатору

Выбор значения из 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 Робот решает эту проблему, используя ряд передовых методов надежного поиска и взаимодействия с компонентами на основе атрибутов и условий, которые не являются динамическими.Затем он предоставляет ярлыки для этого со всеми компонентами HTML, Ext JS и Sencha Touch, с которыми вам придется взаимодействовать.Он представлен в 2 вкусах:

  1. Джава - Знакомый API на основе Selenium и JUnit, в который встроена поддержка веб-драйверов для всех современных браузеров.
  2. Гвен - Язык человеческого стиля для быстрого и простого создания и поддержки браузерных тестов, который поставляется с собственной интегрированной средой разработки.Все это основано на Java API.

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

findExtJsGridRow("Foo");

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

extjsgridrow by text "Foo"

Для обоих существует много документации. Джава и Гвен о том, как работать со специфическими компонентами 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.

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

ItemId можно использовать как альтернативный способ получить ссылку на компонент, когда ссылка на объект недоступна.Вместо использования идентификатора с Ext.getCmp используйте itemId с Ext.container.Container.getComponent, который будет получать идентификаторы или идентификаторы itemId.Поскольку itemId являются индексом внутренней коллекции MixedCollection контейнера, itemId ограничивается локально контейнером, что позволяет избежать потенциальных конфликтов с Ext.ComponentManager, которому требуется уникальный идентификатор.

Переопределение Ext.AbstractComponent's onBoxReady метод, я устанавливаю пользовательский атрибут данных (имя которого происходит от моего пользовательского 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 в качестве параметра в селен.

Например, в 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 в поисках братьев и сестер, элементов спуска/подъема.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top