Sencha Test 使用示例第1部分

介绍


Sencha Test 使得开发者可以编写并在他们的机器上执行测试,然后轻松地在自动化构建系统中进行自动化测试。学习如何编写单元测试最好的方式是看例子。本文我们会过一遍 SenchaTestDemo 项目中的 新建单元测试示例,看下入门是多么简单.

单元测试


不管你使用的是测试驱动开发 (Test Driven Development, TDD),还是行为驱动开发 (Behavioral Driven Development, BDD), 或者是你只想保证你的代码能够正确执行, 单元测试是适用于所有开发者的最佳选择. 单元测试对系统中的最小片段 (即单元) 进行隔离测试,保证他们能正确地运行. 单元测试是一个可以在前期捕获很多bug的方法,极大提升产品质量.

为了说明在真实的应用程序中如何实现, 我们考虑测试应用程序的最小的部分之一: 数据模型(model)。为简单起见, 我们使用 Admin Dashboard 示例中的 待办(TODO)列表 中的 这个model

Admin.model.Todo 模型 中的重要代码部分是下面的 set 函数:

    set: function (name, value) {  
        var data = name;         if (typeof name === 'string') {  
            if (name !== 'done') {  
                data = [name, value];  
            } else {  
                data = [{  
                    completedDate: value ? new Date() : null  
                }];  
                data[0].done = value;  
            }  
        } else {  
            if (data.done !== undefined) {  
                data = [Ext.apply({  
                    completedDate: data.done ? new Date() : null  
                }, data)];  
            } else {  
                data = [name];  
            }  
        }         return this.callParent(data);  
    } 

上面的 set 函数执行一个基本的业务规则:

已完成(done)的待办事项必须有完成时间(completeDate), 未完成的待办的完成时间则为空.

这种方法可以确保在任何用户界面编辑某条记录(record)的 done 字段,都使得 completeDate 字段也会被正确更新。我们如何知道这个方法有效果呢? 写一些单元测试并执行它!

检查下代码, 有四个方面需要考虑: 方法是如何调用的, 哪个字段被设置了, 字段被赋了什么值,以及record的初始状态

不同的调用方式


set 函数有2中方式被调用: 分开传入一个字段名和新的字段值,或者只传入一个键值对。

受影响的字段


set 函数应该只对 done 字段的变化做出反应. 改变其它字段都不会影响 completedDate 字段的值. 修改 done 字段,然后验证 completedDate 字段受到的影响,这个被称为正面测试(positive tests), 而修改其他字段,然后验证 completedDate 字段没有受到影响,称为负面测试(negative tests)。很容易会忘记添加负面测试。要知道, 一个类没有做它不该做的事(负面),和做了它应该做的事 (正面),是同等重要的.

被赋的新值


将 done 字段设置为 true 应该会给 completedData 字段赋值当前日期, 而设置 done 为 false 应该将 completedDate 设为 null.

字段初始值


改变 done 字段的初始值应该会将 done 和 completedDate 标记为被修改过(dirty/modified)。

策略


有很多方法可以结合这些维度并测试每个组合, 但通常都会有一个简单的方式实现一个比较简单的测试套件(test suite)。在这种情况下, 最简单的是先从 record 初始值 这个维度上入手.

   describe('Todo Tests', function() {  
      describe('with a not completed todo item', function() {  
           beforeEach(function () {  
               this.startTime = new Date().getTime();  
               // Create a not completed item  
               this.todo = new Admin.model.Todo({  
                   id: 99,  
                   task: 'Do this now',  
                   done: false  
               });  
           });  

在 Jasmine 单元测试框架中,beforeEach 方法接收一个函数参数,该函数会在该测试套件(suite)的每次测试之前被调用. 上面的代码, Jasmine 提供了一个上下文对象 (“this”指针) ,可以用来存储一条 待办记录(record)实例,其 done 初始值为 false. 因为这是最顶层2个测试套件的其中一个套件的部分逻辑, 所以这个 record 状态 是整个测试组合的前半部分的基石.

第二个维度, 则是调用 set 函数的方式。 剩下的维度就是测试本身了.

describe('set fields by name', function() {  
    it('should not set done or completedDate when changing another field', ...  
    it('should set completedDate when done is set', ...  
    it('should unset completedDate when done is unset', ...  
});  
describe('set fields by object', function() {  
    it('should not set done or completedDate when changing another field', ...  
    it('should complete the todo', ...  
    it('should unset completedDate when done is unset', ...  
});  

非常类似, 但不相同, 主要维度下的第二个字段值也需要重复执行这套测试:

    describe('with a completed todo item', function() {  
        beforeEach( function() {  
            this.startTime = new Date().getTime();  
            this.todo = new Admin.model.Todo({  
                id: 99,  
                task: 'Do this now',  
                done: true,  
                completedDate: new Date()  
            });  
        });  

完整的测试用例, 请看 GitHub.

总结


这个测试套件覆盖了所有正面和负面形式的初始以及值变换的可能性,应该没有其它副作用. 总的来说, set 函数大约20行代码。然而, 因为测试代码的分支性质, 它却花了近250行,才完全覆盖到所有的死角。尽管这250行大多是非常简单的期望语句, 不过测试代码与应用程序代码基本倾向于这个比例。

换句话说,编写单元测试是一种投资, 但却是值得的。下面是一些指导方针, 应该可以帮助实现最小化投资,并使回报最大化。

  • 着眼于棘手的部分。简单的函数可以检查验证。
  • 着眼于重要的部分. 不是所有的代码都和应用程序的整体功能同等重要的。投资在那些你无法承担出错后果的代码上。
  • 着眼于用到的部分. 花费时间测试极少使用的代码,不会有太多回报。不过即使很少使用的代码也有可能非常重要。
  • 测试要简单. 调试一个执行失败的测试的时候,你不会想在复杂的测试代码上挣扎. 再者,你也不想测试代码本身都有bug吧。
  • 正面和负面都要测试. 一定要确保代码不做它不应该做的事情。
  • 着重于输入和输出, 而不是内部细节. 只要输入、公共函数不变,在重写内部逻辑后,测试的输出应该都一致。
  • 确保清理不会被垃圾回收的资源 (比如添加到 DOM 树的元素). 可以使用 afterEach 或者 afterAll 这些 Jasmine 提供的 API,来保证清理工作能正确执行,即使出现了异常。

结论


我希望通过这个单元测试的例子,展示出了创建单元测试的好处,也是相对比较容易的, 可以启发你来编写自己的测试。使用 Sencha Test, 您可以使用Sencha Studio 或 通过命令行 stc 工具在本地运行单元测试。使用 Sencha Test 可以很容易地把测试从开发般到持续集成测试(CI)/构建系统中, 还可以在开发过程中提供安全保障。在本系列的第2部分中, 我将向您展示如何在本地机器上编写和执行测试的一些例子, 然后轻易地复制到持续集成环境(CI)。

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

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