Будь-які пропозиції щодо тестування коду extjs у браузері, бажано із селеном?


92

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

Хтось мав успіх у написанні автоматизованих тестів для веб-сторінок на основі extjs? Багато гуглів знаходить людей з подібними проблемами, але мало відповідей. Дякую!


Хороші люди в Ext JS були досить люб’язними, щоб писати про цю тему у своєму блозі тут . Сподіваюся, це допоможе.
NBRed5,

Відповіді:


173

Найбільша перешкода при тестуванні ExtJS з Selenium полягає в тому, що ExtJS не відображає стандартні елементи HTML, і Selenium IDE буде наївно (і справедливо) генерувати команди, націлені на елементи, які просто виконують роль декору - зайві елементи, які допомагають ExtJS у цілому робочому столі - дивитися і відчувати. Ось декілька порад та підказок, які я зібрав під час написання автоматизованого тесту на селен для програми ExtJS.

Загальні поради

Розташування елементів

При генерації тестових випадків Selenium шляхом запису дій користувача за допомогою IDE Selenium у Firefox, Selenium буде базувати записані дії на ідентифікаторах елементів HTML. Однак для більшості клікабельних елементів ExtJS використовує згенеровані ідентифікатори, такі як "ext-gen-345", які, ймовірно, зміняться під час наступного відвідування тієї ж сторінки, навіть якщо коду не було внесено. Після запису дій користувача для тесту потрібно виконати ручну роботу, щоб пройти всі такі дії, які залежать від сформованих ідентифікаторів, та замінити їх. Існує два типи замін, які можна зробити:

Заміна ідентифікатора ідентифікатора на CSS або XPath Locator

Локатори 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 та повільним рендерингом

Селен має аромати "* 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. *), Які мають асоційовані регулярні вирази або типи типу для перевірки, запускатимуть перевірку з певною затримкою (див. 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"))]

Зверніть увагу, що перевірка "display: none" необхідна, оскільки як тільки поле помилки буде показано, а потім його потрібно буде приховати, ExtJS просто приховає поле помилки, а не повністю видаляє його з дерева DOM.

Поради щодо елементів

Натискання кнопки зовнішньої форми

  • Варіант 1

    Команда: натисніть Ціль: css = button: містить ('Зберегти')

    Вибирає кнопку за її підписом

  • Варіант 2

    Команда: натисніть кнопку Target: css = # save-options button

    Вибирає кнопку за її ідентифікатором

Вибір значення з Ext.form.ComboBox

Command: runScript
Target: with (Ext.getCmp('genderComboBox')) { setValue('female'); fireEvent('select'); }

Спочатку встановлює значення, а потім явно запускає вибрану подію, якщо є спостерігачі.



5

Цей блог мені дуже допоміг. Він досить багато писав на цю тему, і, здається, вона все ще активна. Хлопець також, здається, цінує хороший дизайн.

В основному він говорить про те, як використовувати надсилання javascript для виконання запитів та використання методу Ext.ComponentQuery.query для отримання речей так само, як це робиться у вашому додатку ext внутрішньо. Таким чином, ви можете використовувати xtypes і itemIds, і вам не доведеться турбуватися про спроби проаналізувати будь-який божевільний автоматично згенерований матеріал.

Мені ця стаття стала особливо корисною.

Невдовзі міг би розмістити тут дещо більш детальне - все ще намагаюся зрозуміти, як це правильно зробити


4

Я тестував свою веб-програму ExtJs із селеном. Однією з найбільших проблем був вибір елемента в сітці, щоб щось з ним зробити.

Для цього я написав допоміжний метод (у класі 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) );

Щоб це працювало, мені потрібно встановити свій ідентифікатор у сітці та не дозволяти ExtJs генерувати його власний.


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

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

3

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

Одне розширення Selenium, яке я вважаю корисним, - waitForCondition . Якщо проблема здається проблемою з подіями Ajax, ви можете використовувати waitForCondition, щоб дочекатися подій.


3

Веб-сторінки Ext JS може бути складно перевірити, оскільки через складний HTML вони в кінцевому підсумку генеруються, як із сітками Ext JS.

HTML5 Robot вирішує це питання, використовуючи низку найкращих практик щодо надійного пошуку та взаємодії з компонентами на основі атрибутів та умов, які не є динамічними. Потім він забезпечує ярлики для цього з усіма компонентами HTML, Ext JS та Sencha Touch, з якими вам потрібно буде взаємодіяти. Він поставляється в 2 смаках:

  1. Java - Знайомий API на основі селену та JUnit, який має вбудовану підтримку веб-драйверів для всіх сучасних браузерів.
  2. Гвен - Мова в людському стилі для швидкого та простого створення та підтримання тестів браузера, яка постачається зі своїм інтегрованим середовищем розробки. Все це засноване на Java API.

Наприклад, якщо ви хотіли знайти рядок сітки Ext JS, що містить текст "Foo", ви можете зробити наступне на Java:

findExtJsGridRow("Foo");

... і ви могли зробити наступне в Гвен:

extjsgridrow by text "Foo"

Існує багато документації як для Java, так і для Гвен про те, як працювати зі специфічними компонентами Ext JS. У документації також детально описується отриманий HTML-код для всіх цих компонентів Ext JS, що також може виявитися вам корисним.


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;
    }

2

Простіше тестування за допомогою власних атрибутів даних HTML

З документації Sencha :

ItemId може бути використаний як альтернативний спосіб отримати посилання на компонент, коли посилання на об'єкт недоступне. Замість використання ідентифікатора з Ext.getCmp, використовуйте itemId з Ext.container.Container.getComponent, який буде отримувати itemId або id. Оскільки itemId є індексом внутрішнього MixedCollection контейнера, itemId дістається локально до контейнера - уникаючи потенційних конфліктів з Ext.ComponentManager, який вимагає унікальний ідентифікатор.

Замінюючи метод Ext.AbstractComponent' 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);
  }
});

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


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");
}

1

Для складного інтерфейсу, який не є формальним HTML, xPath - це завжди те, на що можна розраховувати, але дещо складний, коли йдеться про різну реалізацію інтерфейсу за допомогою ExtJs.

Ви можете використовувати 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);

1

Коли я тестував програму ExtJS за допомогою WebDriver, я використовував наступний підхід: я шукав поле за текстом мітки та отримував @forатрибут від label. Наприклад, у нас є ярлик

<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атрибут id та використає його далі. Це, мабуть, найпростіший випадок, але він дає вам спосіб знайти елемент. Набагато складніше, коли у вас немає мітки, але тоді вам потрібно знайти якийсь елемент і написати свій xpath, шукаючи братів і сестер, спускатись / зростати елементи.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.