关于在浏览器中测试extjs代码的任何建议,最好是使用硒?


92

我们一直在成功地使用硒来进行高级网站测试(除了在模块级别进行广泛的python doctest之外)。但是现在我们在很多页面上都使用了extjs,事实证明很难将Selenium测试用于网格等复杂组件。

有没有人成功为基于extjs的网页编写自动化测试?大量谷歌搜索发现存在类似问题的人,但答案却很少。谢谢!


善良的人们在以上的Ext JS已经热情地向发布有关自己的博客上这个主题在这里。我希望这有帮助。
NBRed08年

Answers:


173

使用Selenium测试ExtJS的最大障碍是ExtJS不会呈现标准HTML元素,并且Selenium IDE会天真地(正确地)生成针对仅用作装饰的元素的命令-多余的元素可以帮助ExtJS应用于整个桌面,外观。这是我针对ExtJS应用编写自动Selenium测试时收集的一些技巧。

一般提示

定位元素

通过在Firefox上使用Selenium IDE记录用户操作来生成Selenium测试用例时,Selenium会将记录的操作基于HTML元素的ID。但是,对于大多数可单击的元素,ExtJS使用生成的ID(例如“ ext-gen-345”),即使在未进行任何代码更改的情况下,这些ID在以后访问同一页面时也可能会更改。在记录了用于测试的用户操作之后,需要手动进行所有取决于生成的ID的操作并替换它们。可以进行两种类型的替换:

用CSS或XPath定位器替换ID定位器

CSS定位符以“ css =”开头,而XPath定位符以“ //”开头(“ xpath =”前缀是可选的)。CSS定位器不太冗长,更易于阅读,应优先于XPath定位器使用。但是,在某些情况下,由于CSS定位器根本无法剪切,因此需要使用XPath定位器。

执行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命令触发。这是因为事件侦听器实际上位于容器上,监视其子元素上的鼠标事件,最终冒泡到父对象。选项卡控件是一个示例。要单击选项卡,必须mouseDown在选项卡标签上模拟事件:

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

现场验证

validationDelay在用户输入文本之后或当字段丢失时,具有关联的正则表达式或vtypes进行验证的表单字段(Ext.form。*组件)将以一定的延迟触发验证(请参见默认设置为250ms 的属性)。焦点-或模糊(请参阅validateOnDelay属性)。为了在发出type Selenium命令以在字段内输入一些文本后触发字段验证,您必须执行以下任一操作:

  • 触发延迟验证

    当字段接收到keyup事件时,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

    命令:单击目标:css =#save-options按钮

    通过其ID选择按钮

从Ext.form.ComboBox选择一个值

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

首先设置值,然后在有观察者的情况下显式触发select事件。



5

这个博客对我有很大帮助。他在该主题上写了很多文章,而且似乎仍然很活跃。这家伙似乎也很喜欢出色的设计。

他基本上讨论了使用发送javascript进行查询以及使用Ext.ComponentQuery.query方法以与内部ext应用程序中相同的方式检索内容的方法。这样,您可以使用xtypes和itemIds,而不必担心尝试解析任何疯狂的自动生成的东西。

我发现这篇文章特别有帮助。

可能会在此附近发布一些更详细的信息-仍在努力使我正确理解如何正确执行此操作


4

我一直在用硒测试我的ExtJs Web应用程序。最大的问题之一是在网格中选择一项以对其进行处理。

为此,我编写了辅助方法(在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) );

为此,我需要在网格上设置ID,而不要让ExtJ自己生成ID。


您应该能够通过在Ext.ComponentQuery.query中使用它的xtype来找到该网格(假设您已经从网格扩展并为自己的网格定义了xtype),我对此做了一些介绍。我还发现,您可以通过使用xpath单击包含具有行标识值的td来单击内容
JonnyRaa 2014年

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

Ext JS网页很难测试,因为它们最终会生成复杂的HTML,就像Ext JS网格一样。

HTML5 Robot通过使用一系列最佳实践来解决此问题,这些最佳实践是如何基于非动态的属性和条件可靠地查找组件并与之交互。然后,它提供了用于与所有需要交互的HTML,Ext JS和Sencha Touch组件进行此操作的快捷方式。它有2种口味:

  1. Java的 -熟悉硒和已建成的网络驱动程序支持所有现代浏览器基于JUnit API。
  2. Gwen-一种人类风格的语言,可快速轻松地创建和维护浏览器测试,并带有自己的集成开发环境。所有这些都基于Java API。

例如,如果要查找包含文本“ Foo”的Ext JS网格行,则可以在Java中执行以下操作:

findExtJsGridRow("Foo");

...您可以在Gwen中执行以下操作:

extjsgridrow by text "Foo"

对于Java和Gwen ,都有很多关于如何使用Ext JS特定组件的文档。该文档还详细介绍了所有这些Ext JS组件的结果HTML,您可能还会发现它们很有用。


2

通过页面上的网格ID获取网格的有用技巧:我认为您可以从该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中使用id,而不是在Ext.container.Container.getComponent中使用itemId,它将检索itemId或id。由于itemId是容器内部MixedCollection的索引,因此itemId的作用域是容器的本地范围-避免了与需要唯一ID的Ext.ComponentManager潜在冲突。

覆盖Ext.AbstractComponentonBoxReady方法,我将自定义数据属性(其名称来自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);
  }
});

此方法为开发人员提供了一种在其代码内重用描述性标识符并使​​每次显示页面时这些标识符可用的方法。无需再搜索非描述性动态生成的ID。


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的复杂UI,xPath始终是您可以指望的东西,但是在使用ExtJs进行不同的UI实现时却有点复杂。

您可以将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);

1

当我使用WebDriver测试ExtJS应用程序时,我使用了下一种方法:我按标签文本查找字段,并@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属性中选择id 并进一步使用它。这可能是最简单的情况,但它为您提供了定位元素的方法。如果没有标签,但是需要找到一些元素并编写xpath来寻找同级,下降/上升元素,则要困难得多。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.