关于在浏览器中测试extjs代码的任何建议,最好是使用硒?
我们一直在非常成功地使用selenium来处理高级网站测试(除了在模块级别进行广泛的python doctests之外)。但是,现在我们在很多页面上都使用extjs,事实证明,很难将Selenium测试用于网格等复杂组件。
有没有人成功为基于extjs的网页编写自动化测试?大量的谷歌搜索发现存在类似问题的人,但答案却很少。谢谢!
解决方案
我们能否提供更多有关extjs测试遇到的问题类型的见解?
我发现有用的一个Selenium扩展是waitForCondition。如果问题似乎与Ajax事件有关,则可以使用waitForCondition等待事件发生。
我一直在用硒测试我的ExtJs Web应用程序。最大的问题之一是在网格中选择一项以对其进行处理。
为此,我编写了辅助方法(在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) );
为此,我需要在网格上设置ID,而不要让ExtJ自己生成ID。
使用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
现场验证
在用户输入文本后或者在输入以下内容时立即将具有关联的正则表达式或者vtypes进行验证的表单字段(Ext.form。*组件)以一定的延迟触发验证(请参见" validationDelay"属性(默认设置为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('Save')按其标题选择按钮
- 选项2命令:单击目标:css =#save-options button通过其ID选择按钮
从Ext.form.ComboBox选择一个值
Command: runScript Target: with (Ext.getCmp('genderComboBox')) { setValue('female'); fireEvent('select'); }
首先设置值,然后在有观察者的情况下显式触发select事件。
通过页面上的表格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; }
要检测该元素是否可见,请使用以下子句:
不(包含(@style,"显示:无"))
最好使用这个:
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')]"
对于不是正式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);