本节书摘来自华章出版社《AngularJS深度剖析与最佳实践》一书中的第2章,第2.12节,作者 雪狼 破狼 彭洪伟,更多章节内容可以访问云栖社区“华章计算机”公众号查看

2.12 单元测试

我们在第1章中已经写过两个单元测试(unit test)了,这里我们简单讲一下理论知识。
在Angular中,单元测试的概念和传统的后端编程是一样的。也就是对某些小型功能块儿进行测试,保障其工作逻辑正常。单元测试要尽可能局部化,不要牵扯进很多个模块,必要时可进行mock(模拟)。

2.12.1 MOCK的使用方式

由于JavaScript语言的动态特性,Mock一个普通对象不需要进行特别处理。比如,如果一个测试函数需要访问scope中的一个变量:name,但不用访问$watch等scope的特有函数,那么传入一个普通的哈希对象{name: 'someName'}即可,并不需要new出一个scope来。
除了局部化以外,对单元测试来说,一大挑战是网络操作,如果使用真实的网络操作,那么将带来几个问题:
网络的不稳定性,导致单元测试的不稳定性。想象一个有时成功,有时失败的单元测试,会让程序员多么头疼吧!
网络响应的速度会拖慢整体速度。单元测试执行得必须尽可能快速,如果被迫由于网络操作而变慢,那么一旦多了就会变得很慢,也就会有很多时间浪费在这里。
网络的异步性。虽然异步调用对于单元测试来说并不是不可接受的,但是由于其返回时机不受控制,所以写起来还是比同步调用复杂一些。
另一大挑战是与时间有关的测试。比如一段代码中设置了一小时后触发的定时器,难道我们的单元测试就要一个小时后才能完成?这显然是不合理的。
好在,Angular对网络和定时器等进行了封装,变成了$http、$timeout、$interval等服务。这就意味着,我们只要使用这些内置服务而不是setTimeout等原生函数,那么我们就可以对它们进行Mock,克服上述问题。
对于这些内置服务,Angular提供了一个独立的测试库:angular-mocks.js。
它对Angular的一些内置服务进行了Mock,比如$httpBackend、$timeout、$interval、$exceptionHandler、$log等服务。还提供了一些工具函数,如用于加载模块的module函数、用于依赖注入的inject函数、用于调试的dump函数等,这些函数都是顶层函数,不需要加前缀就可以调用。
但Angular实际上没有Mock $http服务,而是Mock了XHR(XMLHttpRequest)对象,它把原来发送到服务端的Ajax调用,转变成本地调用。这个通过本地调用来模拟服务器的对象则是$httpBackend(模拟http后端,也就是服务器)。

$httpBackend.whenGET('/someUrl').respond({name: 'wolf'}, {'X-Record-Count': 100});

上述语句声明了一个模拟服务端,当被测试代码请求GET /someUrl这个地址时,将被$httpBackend拦截,并返回一个JSON对象{"name": "wolf"},同时,返回一个额外的response header:X-Record-Count,其值为"100"。
注意,我们这里其实只是定义了返回规则,并没有规定啥时候返回这些数据,也就是说,虽然被测代码中的$http函数已经能正确返回我们期望的数据,但目前还不会被触发—直到我们调用了$httpBackend.flush函数。这样,我们就把测试中的异步调用变成了同步调用。
respond中的参数不但可以是一个或两个哈希对象,还可以在前面增加一个返回码,如respond(401, {message: 'Unauthorized'}, {'X-Sign-It': '1887a6b'})等,Angular会自动判断它的数据类型,来决定使用哪种重载形式。如果你需要更多的控制力,还可以转而传入一个函数,其原型是:function(method, url, data, headers) {},这个函数中的四个参数都是由$http请求发来的数据,这个函数的返回值是一个数组,包含状态码、数据等信息,完整的范例如:

$httpBackend.whenPOST('/someUrl').respond(function(method, url, data, headers) {var result = 'Hello, ' + data.name;return [201, result, {'X-Greeting': 'Say Hello'}, 'OK'];
});

这样,当被测代码请求调用$http.post('/someUrl', {name: 'wolf'}),然后调用$httpBackend. flush()时,获得的回应为:状态码201,回应体:Hello, wolf,回应头:X-Greeting: 'Say Hello',同时它的status text为OK。
不过,虽然这种形式很灵活,但对于单元测试来说,还是不应该把mock逻辑写得过于复杂,否则,如果测试代码本身都可能出错,会让你的测试变得非常痛苦。写mock时,推荐的最佳实践是“给出固定数据,返回固定数据”。
如果把上述代码改写为:$httpBackend.whenPOST('/someUrl', {name: 'wolf'}).respond ('Hello, wolf', {'X-Greeting': 'Say Hello'}, 'OK');,不但代码量少了很多,而且更加简洁明确,更能发挥“测试”作为“规约”的作用。
$timeout和$interval的Mock就比较简单了,只是增加了一个flush函数,它的作用和$httpBackend.flush一样,也是立即触发这个异步操作。

2.12.2 测试工具与断言库

我们写好了测试,还要把它跑起来,用来跑测试的工具称为Test Runner,Angular的范例工程中集成的测试工具是Karma,它的用法对写测试来说几乎可以不用管。
而代码中用来写断言的库称为断言库,在范例工程中集成的是jasmine。我们测试代码中的expect和toBe等函数都是来自它的。具体的使用方式可以参见它们的官方文档,此处就不展开讲解了。

《AngularJS深度剖析与最佳实践》一2.12 单元测试相关推荐

  1. 《AngularJS深度剖析与最佳实践》一第1章 从实战开始

    本节书摘来自华章出版社<AngularJS深度剖析与最佳实践>一书中的第1章,作者 雪狼 破狼 彭洪伟,更多章节内容可以访问云栖社区"华章计算机"公众号查看 第1章 从 ...

  2. 《AngularJS深度剖析与最佳实践》一2.2 模块

    本节书摘来自华章出版社<AngularJS深度剖析与最佳实践>一书中的第2章,第2.2节,作者 雪狼 破狼 彭洪伟,更多章节内容可以访问云栖社区"华章计算机"公众号查看 ...

  3. 《AngularJS深度剖析与最佳实践》一2.11 消息

    本节书摘来自华章出版社<AngularJS深度剖析与最佳实践>一书中的第2章,第2.11节,作者 雪狼 破狼 彭洪伟,更多章节内容可以访问云栖社区"华章计算机"公众号查 ...

  4. 《AngularJS深度剖析与最佳实践》一2.10 承诺

    本节书摘来自华章出版社<AngularJS深度剖析与最佳实践>一书中的第2章,第2.10节,作者 雪狼 破狼 彭洪伟,更多章节内容可以访问云栖社区"华章计算机"公众号查 ...

  5. 《AngularJS深度剖析与最佳实践》一2.9 服务

    本节书摘来自华章出版社<AngularJS深度剖析与最佳实践>一书中的第2章,第2.9节,作者 雪狼 破狼 彭洪伟,更多章节内容可以访问云栖社区"华章计算机"公众号查看 ...

  6. 《AngularJS深度剖析与最佳实践》一2.6 指令

    本节书摘来自华章出版社<AngularJS深度剖析与最佳实践>一书中的第2章,第2.6节,作者 雪狼 破狼 彭洪伟,更多章节内容可以访问云栖社区"华章计算机"公众号查看 ...

  7. 《AngularJS深度剖析与最佳实践》一1.3 创建项目

    本节书摘来自华章出版社<AngularJS深度剖析与最佳实践>一书中的第1章,第1.3节,作者 雪狼 破狼 彭洪伟,更多章节内容可以访问云栖社区"华章计算机"公众号查看 ...

  8. 《AngularJS深度剖析与最佳实践》一1.6 实现AOP功能

    本节书摘来自华章出版社<AngularJS深度剖析与最佳实践>一书中的第1章,第1.6节,作者 雪狼 破狼 彭洪伟,更多章节内容可以访问云栖社区"华章计算机"公众号查看 ...

  9. 《AngularJS深度剖析与最佳实践》一1.4 实现第一个页面:注册

    本节书摘来自华章出版社<AngularJS深度剖析与最佳实践>一书中的第1章,第1.4节,作者 雪狼 破狼 彭洪伟,更多章节内容可以访问云栖社区"华章计算机"公众号查看 ...

  10. 《AngularJS深度剖析与最佳实践》一1.5 实现更多功能:主题

    本节书摘来自华章出版社<AngularJS深度剖析与最佳实践>一书中的第1章,第1.5节,作者 雪狼 破狼 彭洪伟,更多章节内容可以访问云栖社区"华章计算机"公众号查看 ...

最新文章

  1. ZStack中的编程技巧
  2. 多部门数据分析需求,如何满足?
  3. new star program
  4. java.nio.file.FileSystemException: xxx: Too many open files
  5. python类加载器_利用Python反序列化运行加载器实现免杀
  6. GitHub 撤销解雇犹太员工决定并公开致歉,涉事 HR 辞职
  7. NSArray遍历和修改崩溃
  8. hdu 3689 Infinite monkey theorem (KMP+DP)
  9. 两台计算机怎样互相访问文件,两台电脑之间怎么互相传文件
  10. css平行四边形与菱形变换
  11. BI是什么意思?在企业应用中有哪些好处?
  12. can和could的用法_Can 和 could 的用法和区别
  13. 华硕主板怎么进入安全模式?
  14. ASP.NET Core中的环境Development、Staging、Production
  15. AIfloki——碾压AIdoge,开启链游新时代
  16. 亲测可用|亚信防毒墙网络版卸载图文教程
  17. win10电脑提示bootmgr is missing的解决方法
  18. c++编程题 甜甜圈大作战
  19. 高瓴资本持仓情况曝光:重新买入小鹏、蔚来,大幅减持哔哩哔哩
  20. 深入浅出Vue.js阅读——整体流程——实例方法与全局API的实现原理

热门文章

  1. 智慧医疗是什么?智慧医院包括哪些方面?
  2. python 封闭图形面积_Python求阴影部分面积
  3. peewee mysql_Peewee、MySQL和INSERT忽略
  4. 129、易燃气体的分级
  5. 2021年汽车驾驶员(技师)考试题及汽车驾驶员(技师)找解析
  6. Java分别使用zxing及qrcode-plugin生成各种样式二维码
  7. Windows11 右键菜单没有新建选项操作
  8. 未分配利润与利润表不一致_资产负债表中的未分配利润和利润表中的净利润肯定是一致的吗?...
  9. 智能时代悄然到来刷脸支付逐渐成为潮流
  10. 基于PL/SQL的数据库备份方法