标签:
原文链接:http://blog.csdn.net/lovelyelfpop/article/details/52301249
英文原文:《Inside the Sencha Test Futures API》
编写交互式测试的一个最大的挑战是处理它们的异步特性。Sencha Test 提供了一个新的强大的API,它被设计用来使这些异步测试像同步方式一样简单。
测试异步代码并不是什么新鲜事, Jasmine 提供了对其开箱即用的支持, 允许将你的测试标识为异步的:
describe(‘Some tests‘, function () { it(‘takes time‘, function (done) { // << "done" marks test async doThings().then(done); }); });
传递给 it()
的第二个参数是一个函数,这个函数中有名字的那个参数(习惯写为“done”),表明 Jasmine 测试是异步的。即测试(最终)告诉 Jasmine, 它是通过调用提供的“done”来结束的,而不是简单地返回。如果测试在一段时间内没有调用
done 函数(默认5秒), 测试则宣告失败。
这个简单的方法在小测试下没问题, 但是如果有多个步骤, 很难猜得到结束之前要消耗的总时间。服务器请求等在测试环境中甚至是不可预知的, 特别是当环境里可能同时装载到多个浏览器和场景下。
当然, 我们很容易忘记调用 done, 但是通常在写测试的时候就会发现。然而,如果一个测试涉及复杂的逻辑, 很可能就会出现有些分支逻辑调用了 done,而某些分支却没有。
it(‘should eventually call done‘, function (done) { if (condition) { // true 98% of the time done(); } else { // oops - fails "randomly" } });
显然,如果可能的话,最好避免写这种分支逻辑。
在交互式测试之前,所有这些问题都是可控的。这种测试不管是在端到端测试应用程序的UI, 还是单元测试应用程序视图和控制器的时候,都是普遍存在的。在这两种情况下, 大多数测试步骤需要异步操作和等待条件, 并混杂着 正确性检查和期望(expectations)。
为了使交互式测试更具表现力和易于维护, Sencha Test 测试提供了ST.future.*
命名空间下的一套类, 统称为“futures”。
Futures 是在测试代码中创建出来的对象, 它提供了一个简洁的语法来描述异步测试序列。来看一个 Futures 的简单的例子, 下面的代码使用 ST.element() 工厂方法返回的一个 ST.future.Element:
it(‘should change text on click‘, function () { ST.element(‘button#foo‘). click(10, 10). textLike(/^Save/). and(function (el) { expect(el.hasCls(‘somecls‘)).toBe(true); }); });
ST.element()
方法接收一个 定位器(locator) (通常是一个字符串,如XPath,
组合查询Component Query, 等等,用于定位一个具体元素). 返回的 ST.future.Element
实例提供了一些方法,我们上面也用到了: click(), textLike() 和 and().
每个方法都返回同样的 ST.future.Element
.
要记住, 虽然 futures 看上去是同步调用, 但是它们实际上并没有立即执行相应操作。相反, 它们创建了一个动作序列,只在“当它们能执行”的时候才会执行。
测试的第一步使用了 ST.element()
创建一个 future 元素实例。这只是该方法的第一个职能, 第二个同样重要的职能是定位到目标 DOM 元素. 在幕后, ST.element()
保存下了定位器,然后会等待目标元素被添加到
DOM 树中并且可见。
等到测试函数将控制权返回给浏览器,定位元素的任务才会开始.
测试的第二步是在元素上执行 click()
,可以使用相对于该元素的坐标 (可选) . 当 click()
被调用,
它把点击事件添加到动作序列中. ST.future.Element
提供的很多方法都是动作方法, 并且它们都以相同的方式工作: 后来的动作会排在之前的动作后面, 它们都会在 future 实例所定位到的元素上执行。
动作方法以动词为名称 (比如“click”).
测试第三步是 textLike()
函数. 此处安排了一次等待,直到元素的 textContent
匹配给定的正则表达式.
这组方法涉及到描述状态,并注入一次延时操作,直到满足目标状态. 有些状态方法需要轮询检测状态变换,而另外一些可以只监听事件来检测变化。不管怎样,这种细节已经被 Sencha Test 处理了,测试者无需关心.
状态方法的名称是名词或者描述 (比如“collapsed”或者“textLike”).
测试的最后一部分是调用 and()
方法. 这个方法安排了一个 函数(function),它会在前一个步骤完成之后,才被调用。此处, 则是在 textContent
满足正则表达式之后。传递给 and()
的函数会接收到2个参数。此处我们只声明了其中一个:
被定位到的 ST.Element
。
可选的第二个参数是 done 函数, 这和 Jasmine 异步测试一样。如果声明了第二个参数, done 函数就会被传递进去, 而且必须被调用。
这些方法通常用于检查元素, 它的当前状态 和/或 应用程序的其他方面。
有时需要在代码中写一个等待条件。我们可以修改 and()
方法,使其接收一个 done 函数.
it(‘should change text on click‘, function () { ST.element(‘button#foo‘). click(10, 10). textLike(/^Save/). and(function (el, done) { // wait for condition and call done() }); });
在其他情况下, 测试必须轮询检测适当的状态。Futures 提供了一个 wait()
方法来处理:
it(‘should change text on click‘, function () { ST.element(‘button#foo‘). click(10, 10). textLike(/^Save/). wait(function (el) { return el.hasCls(‘somecls‘); // return true-like when done }); });
总的来说, 最好使用 and()
方式,因为它避免了轮询。不过更多情况下,还是要依赖具体情况做出正确的选择。
使用 ST.element()
可以很简单地和元素进行异步交互, 不过利用 Sencha Test Futures API 提供的 ST.component()
和
相关类型的 futures 可以做到更多。这些方法创建的类实例,都继承自 ST.future.Component
。这些类扩充了 ST.future.Component
,
并为其相应类型的组件提供了额外的动作和状态方法。
看个新的测试的例子:
it(‘should change cell text on click‘, function () { ST.grid(‘grid#foo‘). row(42). cell(‘firstName‘). reveal(). click(10, 10). textLike(/^Hello$/). and(function (cell) { expect(cell.el.hasCls(‘somecls‘)).toBe(true); }); });
在这个例子中, 位于 ST.grid()
后面的2个调用是定位方法: row()
和 cell()
。有很多方式可以描述目标行和列。这些对应的方法的名字以“row”和“cell”开头。在本例中,
我们使用的方法接收行记录的 id
(42) 和 列id(“firstName”)。
一旦我们调用了 row()
方法, 后面的方法链调用则都在此行(row)之上。我们可以通过调用行(row)的 grid()
方法使操作对象回到原来的
grid,如下。
ST.grid(‘grid#foo‘). row(42). reveal(). // scroll the row into view click(10, 10). grid().row(999). // pick a different row reveal(). click(10, 10);
对 列(cell) 来说也是类似的:
ST.grid(‘grid#foo‘). row(42). cell(‘firstName‘). // column id reveal(). // scroll cell into view click(10, 10). row(). // return to row 42 cell(‘lastName‘). reveal(). click(10, 10). grid().row(999). reveal(). click(10, 10);
组件 futures 层次结构中的类,为 Ext JS 框架中几个最有用的方法,提供了动作方法。比如, ST.future.Panel
提供了 collapse()
和 expand()
动作方法。这些动作方法为合适的
panel 在正确的时机被调用.
组件 futures 还提供了额外的 状态方法. 比如, ST.future.Panel
提供了collapsed()
和 expanded()
状态方法,它们会安排一个延时等待
panel 处于目标状态。
因为ST.future.Component
继承自 ST.future.Element
,
所以它继承得到了大部分动作和状态方法. 这种继承会随着 futures 类的层次结构继续下去. 比如, ST.future.ComboBox
继承自 ST.future.TextField
, ST.future.TextField
又继承自 ST.future.Field
, ST.future.Field
又继承自 ST.future.Component
.
Sencha Test 与 Jasmine 的集成设计允许 futures 与 Jasmine 传统风格的异步测试协调工作。不过, 在使用 futures 时,通常没有必要使用 Jasmine 的 done 函数,就像上面的例子。
当 future 的一系列操作完成后, 测试就即将完成了, Jasmine 会继续下一个测试。还有, future 序列中的每一步,都可以控制自己的超时时间, 没有必要为整个测试确定一个超时时间。因为每个 future 动作也是5秒超时, 所以通常不必显式地设置一个它。
futures API 的另一个优点是,它利用 and()
, 来提供了一种简洁的方式,把异步操作、等待条件和同步检查 混合起来使用。这种做法避免了使用 done 函数, 使测试复杂性降到最低。
Futures 使测试可以遵循 DRY (Don’t Repeat Yourself,避免重复代码) 原则。可以考虑使用下面的方式,而不是在用到的时候才去创建 future 实例.
describe(‘Many tests‘, function () { var Page = { fooGrid: function () { return ST.grid(‘grid#foo‘); }, nameField: function () { return ST.textField(‘textfield[name="username"]‘); } }; it(‘might take some time‘, function () { Page.fooGrid().row(42). reveal(). click(10, 10); }); it(‘might take some more time‘, function () { Page.fooGrid().row(999). reveal(). click(10, 10); }); });
正如上文所述, 我们只是创建了一个包含一组方法的名为“Page”的对象。这种方法使测试封装了测试对象(即应用程序)的定位器。
如果 page 对象在多个测试中都会有用到, 我们可以把它放在 describe()
块外面,
并给它一个更合适的名称。因为 Sencha Test 在这种场景下会加载所有 JavaScript 文件, page 对象将对所有的测试场景都可用。在不同场景中共享 page 对象, 可以把他们添加到测试项目的 附加库列表(Additional Libraries list) 中。
我们希望本文可以让你对 如何使用 Sencha Test Futures API 来解决异步测试的复杂问题,有个初步了解。请看 API 文档 , 查阅所有已经提供的动作和状态方法, 当然, 还可以寻找更多关于未来版本 Ext JS 组件和特性。祝你测试快乐!
欢迎加入Sencha Touch + Phonegap交流群
1群:194182999 (满)
2群:419834979
共同学习交流(博主QQ:479858761)
标签:
原文地址:http://blog.csdn.net/lovelyelfpop/article/details/52301249