来,加入前端自动化单元测试

来,加入前端自动化单元测试


最近闲来无事,开始摸索前端单元测试。一是不备之需,二是确实在实际项目中能够用到单元测试。这样可以提高开发效率,提升代码质量,完全可以单独对 JS 进行测试,无需页面,不依赖其他第三方。

为什么需要单元测试

在这里首先需要知道单元测试的目的及结果:

  • 使代码健壮,质量高,兼容各种临界点;
  • 减少 QA 测试报告的反馈,提高自我影响力;
  • 保证代码的整洁清晰。

如果需要刨根问底追究为什么需要进行单元测试,那我们可以开始讲讲实际项目开发中遇到的一些问题:

  • QA 不断反馈代码有 BUG (此时你正在投入的开发,然后被打扰...);
  • 代码出现 BUG,叠加代码修复 BUG(代码越来越难维护...);
  • 已经开发完成一个模块,但是没有页面提供调试测试;
  • 你开发完成的功能,每次都有许多细小的 BUG(个人影响力下降...)。

好了,列举了这么多原因,相信你也开始心虚了,回去继续搬砖检查检查代码有没有问题,如果你面色从容,大神,请手下我的膝盖。

总结:单元测试的目的只有一个,用来确定是否适合使用

如何进行单元测试

如果明白了为什么要进行单元测试,相信你已经可以开始着手为自己的代码写一些单元测试代码。测试从字面理解就是检验,看对象是否达标,达标就是 pass,不达标就是 fail。产品有这样一个需求,如果结果是 3 达到目标且返回的为有效的数字类型才可以进行比较,下面看个栗子:


  1. /** 
  2.  * 获取 a 除以 b 的结果 
  3.  * @param  {[Number]} a [数字] 
  4.  * @param  {[Number]} b [数字] 
  5.  * @return {[Number]}   [结果数字] 
  6.  */ 
  7. function division(a, b) { 
  8.     return a / b; 
  9.  
  10. // 测试代码 
  11. function test() { 
  12.     var result = division(6, 2); 
  13.      
  14.     if (result === 3) { 
  15.         console.log('pass'); 
  16.     } else { 
  17.         console.log('fail'); 
  18.     } 
  19. }  

咋一看上面的代码没什么问题,可以满足产品的需求,但是问题来了,如果 b 为 0,这个模块就出现了 BUG,同时如果下次需要达到其他的值就算通过,那就得去修改测试代码,这样的测试代码本身也太不健全。于是乎有了下面的方式:


  1. /** 
  2.  * 获取 a 除以 b 的结果 
  3.  * @param  {[Number]} a [数字] 
  4.  * @param  {[Number]} b [数字] 
  5.  * @return {[Number]}   [结果数字] 
  6.  */ 
  7. function division(a, b) { 
  8.  
  9.     if (b === 0) { 
  10.         return 0; 
  11.     } else { 
  12.         return a / b; 
  13.     } 
  14.  
  15. function test(name, result, expect) { 
  16.  
  17.     if (result === expect) { 
  18.         console.log(name + '-> pass'); 
  19.     } else { 
  20.         console.log(name + '-> fail'); 
  21.     } 
  22. test('normal number', division(6, 2), 3); 
  23. test('zero', division(6, 0), 0);  

如果需要期望值为 10 就通过,那可以这样:


  1. test('normal number is 10', division(20, 2), 10); 

单元测试环境搭建及代码示例

但是随着前端迅速的发展,也出现了很多测试框架,下面我演示我在实际项目中使用的测试框架环境配置 karma + jasmine,对于 karma、jasmine 我就不介绍,网上一搜一大把介绍:

1. 安装 node 环境

依赖于 node 作为基础环境,安装完成在控制台运行下面命令查看是否安装成功。


  1. node -v 

2. 新建目录并通过以下命令初始化项目配置 package.json


  1. npm init 

在 package.json scripts: {} 添加以下内容:


  1. "test""karma start karma.conf.js" 

3. 依次安装测试框架


  1. npm install karma -g 
  2. npm install jasmine --save-dev 
  3. npm install karma-jasmine --save-dev 
  4. npm install karma-chrome-launcher --save-dev 
  5. npm install jasmine-core --save-dev  

或者一次性安装


  1. npm install karma -g 
  2.  
  3. npm install jasmine karma-jasmine karma-chrome-launcher jasmine-core --save-dev  

运行以下命令新建 karma.conf.js(根目录下不是必须)


  1. karma init 

文件内容及说明:


  1. /** 
  2.  * karma 自动化测试参数配置 
  3.  */ 
  4.  
  5. module.exports = function(config) { 
  6.     config.set({ 
  7.  
  8.         // 基础路径,用在files,exclude属性上 
  9.         basePath: ''
  10.  
  11.         // 可用的测试框架: https://npmjs.org/browse/keyword/karma-adapter 
  12.         frameworks: ['jasmine'], 
  13.  
  14.         // 需要加载到浏览器的文件列表 
  15.         files: [ 
  16.             './src/**/*.js'
  17.             './test/unit/specs/*.spec.js' 
  18.         ], 
  19.  
  20.         // 排除的文件列表 
  21.         exclude: [ 
  22.             'karma.conf.js' 
  23.         ], 
  24.  
  25.         // 在浏览器使用之前处理匹配的文件 
  26.         // 可用的预处理: https://npmjs.org/browse/keyword/karma-preprocessor 
  27.         preprocessors: {}, 
  28.  
  29.         // 使用测试结果报告者 
  30.         // 可能的值: "dots""progress" 
  31.         // 可用的报告者: https://npmjs.org/browse/keyword/karma-reporter 
  32.         reporters: ['progress'], 
  33.  
  34.         // web server port 
  35.         port: 9876, 
  36.  
  37.         // 启用或禁用输出报告或者日志中的颜色 
  38.         colors: true
  39.  
  40.         /** 
  41.          * 日志等级 
  42.          * 可能的值: 
  43.          * config.LOG_DISABLE //不输出信息 
  44.          * config.LOG_ERROR    //只输出错误信息 
  45.          * config.LOG_WARN //只输出警告信息 
  46.          * config.LOG_INFO //输出全部信息 
  47.          * config.LOG_DEBUG //输出调试信息 
  48.          */ 
  49.         // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 
  50.         logLevel: config.LOG_INFO, 
  51.  
  52.         // 启用或禁用自动检测文件变化进行测试 
  53.         autoWatch: true
  54.  
  55.         // 测试启动的浏览器 
  56.         // 可用的浏览器: https://npmjs.org/browse/keyword/karma-launcher 
  57.         browsers: ['Chrome'], 
  58.  
  59.         // 开启或禁用持续集成模式 
  60.         // 设置为true, Karma将打开浏览器,执行测试并最后退出 
  61.         singleRun: false
  62.  
  63.         // 并发级别(启动的浏览器数) 
  64.         concurrency: Infinity, 
  65.  
  66.         // 依赖插件 
  67.         plugins: [ 
  68.             'karma-chrome-launcher'
  69.             'karma-jasmine' 
  70.         ] 
  71.     }) 
  72. }  

5. 新建源代码及测试代码目录,目录结构如下:


  1. project 
  2.     - node_modules 
  3.         - *(node 模块) 
  4.     - src 
  5.         - FQA 
  6.             - index.js 
  7.     - test 
  8.         - unit 
  9.             - specs 
  10.                 - *.spec.js 
  11.     - karma.conf.js 
  12.     - package.json  

6. 测试代码

。index.js 源码


  1. /** 
  2.  - test map method callback and parseInt param use 
  3.  - @return {[Array]} [Array] 
  4.  */ 
  5. function checkMap() { 
  6.     var nums = ['1''2''3']; 
  7.  
  8.     return nums.map(parseInt); 
  9.  
  10. /** 
  11.  - test null is Object,and common object is same 
  12.  - @return {[Array]} [Array] 
  13.  */ 
  14. function typeofAndInstanceOf() { 
  15.     var result = []; 
  16.     result.push(typeof null); 
  17.     result.push(null instanceof Object); 
  18.  
  19.     return result; 
  20.  
  21. /** 
  22.  - 检测操作符优先级 
  23.  - @return {[string]} [返回字符串] 
  24.  */ 
  25. function checkOperators() { 
  26.     var result = 'autoTest'
  27.     result = 'Value is ' + (result === 'autoTest') ? 'Something' : 'Nothing'
  28.  
  29.     return result; 
  30. }  

。fqa.spec.js 测试代码


  1. /** 
  2.  - test index.js checkMap method 
  3.  - detail: 
  4.  -     parseInt(val, base), base is 2 ~ 36, otherwise value equal NaN. 
  5.  */ 
  6. describe('test map and callback parseInt'function() { 
  7.      
  8.     it('a array call map'function() { 
  9.         var nums = checkMap(); 
  10.         console.log(nums); 
  11.  
  12.         expect([1, NaN, NaN]).toEqual(nums); 
  13.     }); 
  14. }); 
  15.  
  16. /** 
  17.  - test index.js typeofAndInstanceOf method 
  18.  - detail: 
  19.  -     typeof null qeual 'object', but null instanceof Object equal false, because null Constructor not Object. 
  20.  */ 
  21. describe('test null is object'function() { 
  22.      
  23.     it('null object'function() { 
  24.         var result = typeofAndInstanceOf(); 
  25.         console.log(result); 
  26.  
  27.         expect(['object'false]).toEqual(result); 
  28.     }); 
  29. }); 
  30.  
  31. /** 
  32.  - test index.js checkOperators method 
  33.  - detail: 
  34.  -     compare operator precedence, + gt ?. 
  35.  */ 
  36. describe('test null is object'function() { 
  37.  
  38.     it('test operator preceence'function() { 
  39.         var result = checkOperators(); 
  40.         console.log(result); 
  41.  
  42.         expect('Something').toEqual(result); 
  43.     }); 
  44. });  

7. 运行 sudo npm run test 执行测试代码


  1. "scripts": { 
  2.     "test""karma start karma.conf.js" 
  3. }  

结果:

来,加入前端自动化单元测试

解答

1. npm run test 运行的实际上是 package.json 中配置的命令:


  1. "test""karma start karma.conf.js" 

2. describe 定义测试模块,it 测试一个单元,describe 内部可以同时定义多个 it,因此可以做一系列的单元测试,测试方法详见官方文档。

3. karma.conf.js 配置 files 设置测试时需要被加载的文件


  1. files: [ 
  2.     './src/**/*.js'
  3.     './test/unit/specs/*.spec.js' 

总结

希望看完这篇文章,你也能够动起手来,开始编写一些单元测试代码,提高代码的质量,提升自己的周围影响力。本篇文章内容表述了实际项目开发中会遇到的问题,我们可以通过单元测试来减少这类问题的发生,以提高代码的安全性,代码的质量,从而保证产品的稳定性。点击此处查看更多文章。


作者:佚名

来源:51CTO

上一篇:带你读《基于浏览器的深度学习 》之二:神经网络架构


下一篇:带你读《JVM G1源码分析和调优》之三:G1的对象分配