自动化测试——使用CasperJS/PhantomJS对ExtJS web应用程序进行UI测试



我正在测试ExtJS web应用程序的UI,我是一个初学者。我试图通过使用CasperJS/PhantomJS工具测试ExtJS小部件。此外,我使用revive生成所需的CasperJs脚本,并对其进行必要的更改。

由于ExtJs动态地为它创建的DOM元素生成唯一的id,我想知道如何在CasperJs脚本中为测试提供这些id。

例如,下面的Casper脚本是由resurrection生成的:

   casper.waitForSelector("#ext-gen1142 .x-tree-icon.x-tree-icon-parent",
       function success() {
           test.assertExists("#ext-gen1142 .x-tree-icon.x-tree-icon-parent");
           this.click("#ext-gen1142 .x-tree-icon.x-tree-icon-parent");
       },
       function fail() {
           test.assertExists("#ext-gen1142 .x-tree-icon.x-tree-icon-parent");
   });
   casper.waitForSelector("#gridview-1038",
       function success() {
           test.assertExists("#gridview-1038");
           this.click("#gridview-1038");
       },
       function fail() {
           test.assertExists("#gridview-1038");
   });

这里#ext-gen1142和#gridview-1038是动态创建的id。在测试中应该如何提供数据?是否有任何存根或模拟工具在代码中与ExtJs一起工作,以便在测试期间运行时提供这些id ?

我遇到了SinonJS。可以使用它吗?或者我需要使用这个答案中提到的CSS或XPath定位器吗?使用CSS或Xpath定位器有多可靠?

提前感谢!

回答这个问题并不容易,但这里有一些想法…

  1. 不要依赖生成的id。从来没有。如果你运气好,它们会在你不喜欢的时候改变。

  2. 你最好的朋友可能是你附加到组件上的伪CSS类。您也可以使用id,但这只有在页面中只出现一次的元素时才合理。如果是这样的话,它们是开始选择/查询的非常好的锚。

  3. 使用ExtJS可以使用
  4. XPath,但是必须仔细选择元素。ExtJS在生成小的东西时非常冗长,因此您的路径可能相当复杂。当Sencha放弃对有问题的浏览器(IE <8)也许他们改变了模板,你的XPath找不到任何东西。

  5. SinonJS很棒。但是它对DOM问题没有多大帮助。但是你可以在测试中使用它。我想它将在测试控制器或非平凡模型的部分中获得最大回报。

  6. 根据实际UI组件和屏幕部分对测试组件建模。不要只是记录一个脚本。测试代码应该像产品代码一样设计。如果您创建了测试代码和逻辑的可重用组件,您就不必担心更改。在最好的情况下,一个组件中的更改只会触及该特定组件的测试代码。

  7. 我知道你有ExtJS。但是花点时间看看AngularJS,看看测试JavaScript web应用程序的所有部分是多么容易。我并不是说你应该切换到AngularJS,但是你可以从中学到很多。看看Deft JS,因为它有很多增强ExtJS应用程序可测试性的概念。

我使用Siesta进行ExtJs测试。它对所有JavaScript(基于jQuery和其他)都非常有效,但它是专门为ExtJS/Sencha Touch设计的。

它具有将CSSquery和ComponentQuery结合起来选择元素的功能,我认为这将为您解决许多问题。

在付费版本中,甚至有一个测试记录器来记录场景并将它们用于您的测试。

这是一个演示

下面是一些示例代码:

StartTest(function(t) {
    t.chain(
        { waitFor : 'CQ', args : 'gridpanel' },
        function(next, grids) {
            var userGrid = grids[0];
            t.willFireNTimes(userGrid.store, 'write', 1);
            next();
        },
        { waitFor : 'rowsVisible', args : 'gridpanel' },
        { action : 'doubleclick', target : 'gridpanel => .x-grid-cell' },
        // waiting for popup window to appear
        { waitFor : 'CQ', args : 'useredit' },
        // When using target, >> specifies a Component Query
        { action : 'click', target : '>>field[name=firstname]'},
        function(next) {
            // Manually clear text field
            t.cq1('field[name=firstname]').setValue();
            next();
        },
        { action : 'type', target : '>>field[name=firstname]', text : 'foo' },
        { action : 'click', target : '>>useredit button[text=Save]'},
        function(next) {
            t.matchGridCellContent(t.cq1('gridpanel'), 0, 0, 'foo Spencer', 'Updated name found in grid');
        }
    );
})    

最新更新