Sencha Test 使用示例第2部分

介绍


Sencha Test 给开发者和测试工程师提供了一个完整的平台来为单页面应用程序执行功能测试. 测试可以在工程师本地机器上编写并执行,然后很简单地复制到持续集成(CI) 环境中. 和本系列第1部分一样,最好的说明方式就是过一遍 这些例子.

功能测试


功能测试是黑盒测试的一种形式,确保系统满足功能需求. 功能测试加载应用程序,并通过特定的工作流程驱动,确保系统做它应该做的事情. 考虑到异步JavaScript 和 web应用程序的性质, 为保证正确地测试,这些工作流通常会很耗时。

下面我们会看一下这些例子,了解 Sencha Test 是如何使用 Futures API 来简化这些流程的. 更多信息, 请看这篇关于 Futures 的文章.

应用程序


本文使用来自于 Ext JS 6 的 Admin Dashboard 应用程序模板,来作为测试对象 (请看在线示例).

测试用例的结构


Admin Dashboard 应用程序的测试用例按照下面三种场景进行组织: 单元(Unit), 功能(Functional), 和 集成(Integration). 功能(Functional)场景下的测试用例,根据应用程序本身的导航结构,进一步组织成文件和文件夹形式。

测试视图(Views)


在这个单页面应用程序中, 每一个 View 都有一个单独的和它相关的文件,以及它的测试文件. 在这个测试文件夹中,每一个 .js 文件都是一个套件 (suite), 因为它可以包含很多个测试用例。我们来看看在 功能(Functional) 场景下的 adminDashboard.js .

入口


为了确保套件中的每个测试都有一个统一的入口,Jasmine 提供了一个 beforeEach 方法:

beforeEach(function(){  
    Admin.app.redirectTo("#dashboard");  
});  

这是很重要的,如果各个测试都通过 tree list 改变了路由(Route). 我们无需担心每次开始执行测试的时候,应用程序都处于"随机"的状态.

页面对象(The Page Object)
每个 View 都由很多控件组成,这些控件可能会在多个不同的测试中都会用到. 这个技术避免了重复的代码,叫做 “页面对象(page object)” (可在 “Keeping DRY”这篇文章中获得更多内容). 在测试套件的顶部,就是这个 page object,里面有一些常用的函数,如下:

var Dash = {  
treeList: function () {  
    return ST.component('treelist');  
},  
  
hamburger: function () {  
    return ST.component('#main-navigation-btn');  
},  
  
menuItem: function (itemName) {  
    return ST.component('treelistitem[text=' + itemName + ']');  
},  
  
toolbarItem: function (itemName) {  
    return ST.button('toolbar button[href=#' + itemName + ']');  
},  
  
// ...  

page object 里面的函数返回的是 future,用来执行接下来的各种测试. 有一些函数 (比如treeList) 没有参数,返回特定组件的 future. 其它函数 (比如menuItem) 接受一个参数用来组装 特定控件的定位器(locator). 这些函数使得测试代码的可读性和可维护性更高。

定位控件的艺术


也许功能测试最难的部分是唯一识别到所需的组件。Page objects通过一套函数将这一问题隐藏在幕后。这样,如何定位到页面上一个特定的控件这些细节将不再重复。如果应用程序更改了,函数出问题了,那么只需要改进一个地方即可.

当然, 创建稳定的功能测试的最好的方法是和开发团队一起制定一个定位控件的策略。没有这种合作,整个测试套件将永远处于“重构”浪潮的失败之中。

定位器(Locators)


Page Object 中的每个函数都有2个部分. 请看:
hamburger: function () {  
    return ST.component('#main-navigation-btn');  
},  

这两个关键部分是: 调用的 API 函数,用来创建正确的类型的 future (这里就是“ST.component”函数); 然后是传递给该函数的“定位器(locator)” 字符串 (“#main-navigation-btn”). 如何选择 API 函数,应该需要根据对组件类型的知识来决定. 然而,定位器(locator)字符串, 需要更多考虑. 定位器有2种通用格式,还有一种额外的格式为了简洁设计和性能考虑。

组合查询


组合查询语法是 Ext JS 组件查询(Component Query) 语法 和 CSS 语法(用于 DOM API) 的组合. 它们都使用相同的基本语法,来描述控件或元素. 这是推荐的定位器字符串格式,因为它和 Ext JS 应用程序中的组件的结构和组件渲染出来的元素一一映射.

理解组合查询的最简单的方式是学习 CSS. 写 CSS 选择器的时候, 你结合 元素标签(tag)名称, 类名, 和 属性过滤,来选择匹配的元素。比如:

div.foo[title="Hello"]  

上面的代码选择了一个 Ext JS panel 控件,它的title属性值为“Hello”. 当这两段结合在一起 (使用“=>” 分隔), 你就得到了一个组合查询:

panel[title="Hello"] => div.foo  

上面的代码选取了一个具有类名“foo”的“div”, 并且是“title” 为“Hello”的“panel”的子元素.

XPath


第二个常用的格式是 XPath. 当定位器字符串以“/”字符开头的时候,会被识别为一个 XPath,而不是组合查询. XPath只适用于元素层面, 因此用于Ext JS组件结构的时候会变得复杂。XPath的详细使用已经超出了本文的范围, 但是一个简单的例子如下:
//div[@id="mainDiv"]/span[2]  

上面的代码寻找“id”属性为“mainDiv”的第二个直接子span元素.

At-Path


“at-path” 语法是 XPath 的一个很小的子集,对 path 的第一段做了一些变换。上述 XPath 等同于下面的 at-path:
@mainDiv/span[2]

at-path 以“@” 字符开头,然后是元素的id. 可选地, 紧随其后的可以是一系列的斜杠,然后是标记(tag)名称和顺序。定位器的形式很显然更多的密度比XPath和很有效地找到开始元素。从那里,也很简单。 这种定位器方式显然比 XPath 更紧凑,更容易找到起始元素. 接着, 往下查找元素也就变得简单了。

这种格式只适用于当应用程序给重要组件或元素提供了有意义的id的时候.

编写测试


使用 futures 和 page object, 各个测试会变得很简洁,一目了然. 看下面的测试代码:
it('click to expand menu item', function(){  
    Dash.menuItem('Pages')  
        .and(function(menuItem){  
            expect(menuItem.isExpanded()).toBe(false);  
        })  
        .click()  
        .wait(function (menuItem) {  
            return menuItem.isExpanded();  
        })   
        .and(function(menuItem){  
            expect(menuItem.isExpanded()).toBe(true);  
        });  
});  

逻辑流程从上到下. 尽管行为在一段时候后发生 (比如, 在动画结束的时候), 测试用例编写人员却不需要关心这些延迟。

结论


如果你已经阅读了本系列的第1部分 , 你会发现单元测试和功能测试有很多共同的部分. Jasmine 和 Sencha Test APIs, 以及 测试运行器(test runner), 都是共享的. 所以不管你是否开始单元测试或功能测试, 你总是可以涉足其他形式,而不用安装新的工具或学习新的api。

想看更多其他的功能测试和它们所使用的技术, 请转至GitHub上的完整示例。想观看测试同时运行在多个浏览器中, 请看这个测试 Admin Dashboard 的视频.

译者: 神秘_博士
原文: http://blog.csdn.net/lovelyelfpop/article/details/52293285

作者: Don Griffin
英文原文: https://www.sencha.com/blog/sencha-test-examples-part-2/