zoukankan      html  css  js  c++  java
  • 学习Karma+Jasmine+istanbul+webpack自动化单元测试

    学习Karma+Jasmine+istanbul+webpack自动化单元测试

    1-1. 什么是karma?
      Karma 是一个基于Node.js的Javascript测试执行过程管理工具。该工具可用于测试所有主流web浏览器,也可集成到CI工具,也可以
    和其他代码编辑器一起使用,它可以监听文件的变化,然后自动执行。

    1-2. 什么是Jasmine?
    Jasmine也是一款javascript测试框架。Jasmine官网文档地址(https://jasmine.github.io/2.3/introduction.html)

    1-3. 什么是istanbul?
    istanbul 是一个单元测试代码覆盖率检查工具,它可以直观的告诉我们,单元测试对代码的控制程度。

    2. 安装Karma环境
    如下命令:

    npm install -g karma

    为了方便搭建karma环境,我们可以全局安装karma-cli来帮我们初始化测试环境。
    如下命令安装:

    npm install -g karma-cli

    我们在项目的根目录下也需要安装一下,如下命令:

    npm install karma --save-dev

    如上,安装完成以后,我们在项目的根目录下的命令行输入命令:karma start
    运行如下:

    karma start
    08 02 2018 21:38:49.566:WARN [karma]: No captured browser, open http://localhost:9876/
    08 02 2018 21:38:49.572:INFO [karma]: Front-end scripts not present. Compiling...
    08 02 2018 21:38:50.257:INFO [karma]: Karma v2.0.0 server started at http://0.0.0.0:9876/

    然后我们在浏览器输入 http://localhost:9876, 如下图所示:

    如果出现以上信息,表示karma已经安装成功了。

    3. karma的配置
    命令如下:

    karma init

    对上面命令的说明如下:
    1. 使用哪个测试框架,我们选择了jasmine
    2. 是否添加Require.js插件,我们选择no,不添加。
    3. 选择浏览器,我们选择了chrome。
    4. 测试文件路径设置,文件可以使用通配符匹配,比如*.js匹配指定目录下所有的js文件。
    5. 在测试文件路径下,需要被排除的文件。
    6. 是否允许karma监测文件,yes表示当测试文件变化时候,karma会自动测试。

    如上命令后,就会在项目的根目录下 生成 karma.conf.js 文件;
    下面对常用的 karma.conf.js 配置项进行解析说明如下:

    // Karma configuration
    // Generated on Thu Feb 08 2018 10:39:50 GMT+0800 (CST)
    
    module.exports = function(config) {
      config.set({
    
        // 将用于解析所有模式的基本路径
        basePath: '',
    
    
        // 选择测试框架,我们选择 'jasmine'
        // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
        frameworks: ['jasmine'],
    
    
        // 在浏览器中加载的匹配的文件列表。
        files: [
          /* 注意:是自己添加的 */
          'src/**/*.js',
          'test/**/*.js'
        ],
    
    
        // 要排除的文件列表
        exclude: [],// 在将其提供给浏览器之前,预处理匹配的文件,
        // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
        preprocessors: {
    
        },// 怎么显示测试结果 
        // 测试结果显示插件: https://npmjs.org/browse/keyword/karma-reporter
        reporters: ['progress'],
    
    
        // 服务器的端口号
        port: 9876,
    
        // 在输出中启用/禁用颜色(记录reporters和日志), 
        colors: true,
    
    
        // 显示日志记录的级别(默认就好)
        // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
        logLevel: config.LOG_INFO,
    
    
        // 当任何测试文件更改时候,启用/禁用监听文件并执行测试
        autoWatch: true,
    
    
        // start these browsers  启动的浏览器chrome
        // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
        browsers: ['Chrome'],
    
    
        // 持续集成模式,默认就好
        // if true, Karma captures browsers, runs the tests and exits
        singleRun: false,
    
        // 并发级别,可以同时启动多少个浏览器,默认无限大
        // how many browser should be started simultaneous
        concurrency: Infinity
      })
    }

    如上代码中的 config.set 中的 files 配置的  是我自己添加的;如下代码:

    files: [
      /* 注意:是自己添加的 */
      'src/**/*.js',
      'test/**/*.js'
    ]

    它的作用是:就是把需要测试的文件都require进来,然后一股脑的在browsers里面跑。

    4. 开启Karma
    命令如下: karma start
    手动打开chrome,输入localhost:9876 即可打开了。
    在控制台命令行中看到如下信息 说明运行成功了。

    ~/个人demo/vue1204/karma-demo on � Dev_20171115_wealth! 
    $ karma start
    08 02 2018 11:06:41.365:WARN [karma]: No captured browser, open http://localhost:9876/
    08 02 2018 11:06:41.379:INFO [karma]: Karma v2.0.0 server started at http://0.0.0.0:9876/
    08 02 2018 11:06:41.380:INFO [launcher]: Launching browser Chrome with unlimited concurrency
    08 02 2018 11:06:41.386:INFO [launcher]: Starting browser Chrome
    08 02 2018 11:06:42.457:INFO [Chrome 64.0.3282 (Mac OS X 10.11.6)]: Connected on socket JUZvceJFJhGgWFYJAAAA with id 7542403
    LOG: 111
    Chrome 64.0.3282 (Mac OS X 10.11.6): Executed 1 of 1 SUCCESS (0.011 secs / 0 secs)

    运行后会自动打开chrome浏览器,http://localhost:9876/?id=50037613 

    先来安装一下依赖的插件如下:

    1. 需要可以打开chrome浏览器的插件 npm install karma-chrome-launcher --save-dev
    2. 需要可以运行jasmine的插件 npm install karma-jasmine jasmine-core --save-dev
    3. 需要可以运行webpack的插件 npm install karma-webpack webpack --save-dev
    4. 需要可以显示的sourcemap的插件 npm install karma-sourcemap-loader --save-dev
    5. 需要可以显示测试代码覆盖率的插件 npm install karma-coverage-istanbul-reporter --save-dev
    6. 需要全局安装 jasmine-core 如命令:npm install -g jasmine-core
    如下一键安装命令:

    npm install --save-dev karma-chrome-launcher karma-jasmine karma-webpack karma-sourcemap-loader karma-coverage-istanbul-reporter

    也需要全局安装一下 jasmine-core, 如下代码:

    npm install -g jasmine-core

    我们可以写一个简单的测试用例;如下目录结构:

    karma-demo
        |---src
        |  --index.js
        |--- test
        | |-- index.test.js
        |
        |--- karma.conf.js

    src/index.js代码如下:

    function isNum(num) {
      if (typeof num === 'number') {
        return true;
      } else {
        return false;
      }
    }

    test/index.test.js代码如下:

    describe('测试用例编写', function() {
      it('isNum() should work fine', function() {
        console.log(111)
        expect(isNum(1)).toBe(true);
        expect(isNum('1')).toBe(false);
      });
    });

    如上代码我们可以看到 在 test/index.test.js 里面我们调用了 isNum方法,但是并没有使用require引用进来而可以使用,那是因为
    我们的karma.conf.js里面的配置文件 files里面设置了,因此没有使用require就可以使用了。

    files: [
    /* 注意:是自己添加的 */
    'src/**/*.js',
    'test/**/*.js'
    ]

    它的作用是:就是把需要测试的文件都require进来,然后一股脑的在browsers里面跑。

    5. Coverage
    如何测量测试脚本的质量呢?其中有一个参考指标就是代码覆盖率。
    5-1:什么是代码覆盖率?
    代码覆盖率是在测试中运行到的代码占所有代码的比率。因此接下来我们在Karma环境中添加Coverage。
    在项目的根目录下,运行如下命令进行安装

    npm install --save-dev karma-coverage

    然后需要在配置文件 karma.conf.js 代码配置如下:

    module.exports = function(config) {
      config.set({
    
        // base path that will be used to resolve all patterns (eg. files, exclude)
        basePath: '',
    
        // frameworks to use
        // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
        frameworks: ['jasmine'],
    
    
        // list of files / patterns to load in the browser
        files: [
          /* 注意:是自己添加的 */
          'src/**/*.js',
          'test/**/*.js'
        ],
    
    
        // list of files / patterns to exclude
        exclude: [],
    
    
        // preprocess matching files before serving them to the browser
        // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
        /* 覆盖源文件 不包括测试库文件*/
        preprocessors: {
          'src/**/*.js': ['coverage']
        },
        
    
        // test results reporter to use
        // possible values: 'dots', 'progress'
        // available reporters: https://npmjs.org/browse/keyword/karma-reporter
        reporters: ['progress', 'coverage'],
        
        /* 新增的配置项 */
        coverageReporter: {
          type: 'html',
          dir: 'coverage/'
        },
    
        // web server port
        port: 9876,
    
    
        // enable / disable colors in the output (reporters and logs)
        colors: true,
    
    
        // level of logging
        // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
        logLevel: config.LOG_INFO,
    
    
        // enable / disable watching file and executing tests whenever any file changes
        autoWatch: true,
    
    
        // start these browsers
        // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
        browsers: ['Chrome'],
    
    
        // Continuous Integration mode
        // if true, Karma captures browsers, runs the tests and exits
        singleRun: false,
    
        // Concurrency level
        // how many browser should be started simultaneous
        concurrency: Infinity
      })
    }

    具体的配置可以参考  https://www.npmjs.com/package/karma-coverage

    再运行下 karma start后,会在根目录下生产 coverage目录,里面有index.html作为本次的测试报告,我们打开看下,如下:

    如上代码的覆盖率是对源文件需要被测试的覆盖率是100%;
    要生成代码覆盖率,可以看这篇文章(http://karma-runner.github.io/0.8/config/coverage.html
    想要生成覆盖率,需要在配置项配置如下三个选项:
    1. preprocessors coverage (必须配置的)
    2. reporters coverage (必须配置的)
    3. reporter options (可选的)

    1. Preprocessors(配置预处理器)
    preprocessors 的含义是:是那些测试文件需要被覆盖,比如,如果所有代码都在src/下的文件 您需要添加到您的配置文件,如上
    配置代码:

    preprocessors: {
      'src/**/*.js': ['coverage']
    }

    注意: 不要包含你所依赖的库,测试文件等等,下面就是一个错误的配置信息。

    files = [
      JASMINE,
      JASMINE_ADAPTER,
      'lib/*.js',
      'test/*.js'
    ];
    preprocessors = {
      '**/*.js': 'coverage'
    };

    2. Reporters(配置报告)
    在配置文件中包含下面的信息来激活覆盖率报告器。
    如上配置代码:

    reporters: ['progress', 'coverage'],

    这样将会对每个浏览器创建一个覆盖率报告,另外,它还会创建一个 Json 文件,其中包含输出的中间数据。

    3. Reporter Options(配置报告选项)
    默认的报告格式如下:

    coverageReporter: {
      type: 'html',
      dir: 'coverage/'
    },

    type 是一个字符串值,取值可以是:
    html (default)
    lcov (lcov and html)
    lcovonly
    text
    text-summary
    cobertura (xml format supported by Jenkins)
    dir 则用来配置报告的输出目录。
    如果类型是 text 或者 text-summary,你可以配置 file 参数来指定保存的文件名。

    coverageReporter = {
      type : 'text',
      dir : 'coverage/',
      file : 'coverage.txt'
    }

    如果没有文件名,就会输出到控制台。

    6. webpack和Babel集成Karma环境中。
    在项目中,会使用到webpack和es6,因此需要集成到karma环境中。
    安装karma-webpack

    npm install --save-dev karma-webpack webpack

    1.安装babel核心文件 npm install babel-core --save-dev
    2. webpack的Loader处理器 npm install babel-loader --save-dev
    3. babel的istanbul覆盖率插件 npm install babel-plugin-istanbul --save-dev
    4. babel转换到哪个版本这里是ES2015 npm install babel-preset-es2015 --save-dev

    一键安装命令如下:

    npm install --save-dev babel-loader babel-core babel-preset-es2015 babel-plugin-istanbul

    然后 src/index.js 代码变成如下:

    function isNum(num) {
      if (typeof num === 'number') {
        return true;
      } else {
        return false;
      }
    }
    
    module.exports = isNum;

    test/index.test.js 变成如下:

    const isNum = require('../src/index');
    
    describe('测试webpack+babel集成到Karma中', () => {
      it('isNum() should work fine.', () => {
        expect(isNum(1)).toBe(true);
        expect(isNum('1')).toBe(false);
      })
    });

    接下来修改配置文件karma.conf.js 如下配置代码:

    module.exports = function(config) {
      config.set({
    
        // base path that will be used to resolve all patterns (eg. files, exclude)
        basePath: '',
    
    
        // frameworks to use
        // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
        frameworks: ['jasmine'],
    
    
        // list of files / patterns to load in the browser
        files: [
          /* 注意:是自己添加的 */
          'test/**/*.js'
        ],
    
    
        // list of files / patterns to exclude
        exclude: [],
    
    
        // preprocess matching files before serving them to the browser
        // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
        preprocessors: {
          'test/**/*.js': ['webpack', 'coverage']
        },
    
    
        // test results reporter to use
        // possible values: 'dots', 'progress'
        // available reporters: https://npmjs.org/browse/keyword/karma-reporter
        reporters: ['progress', 'coverage'],
    
        /* 新增的配置项 */
        coverageReporter: {
          type: 'html',
          dir: 'coverage/'
        },
    
        // web server port
        port: 9876,
    
    
        // enable / disable colors in the output (reporters and logs)
        colors: true,
    
    
        // level of logging
        // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
        logLevel: config.LOG_INFO,
    
    
        // enable / disable watching file and executing tests whenever any file changes
        autoWatch: true,
    
    
        // start these browsers
        // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
        browsers: ['Chrome'],
    
    
        // Continuous Integration mode
        // if true, Karma captures browsers, runs the tests and exits
        singleRun: false,
    
        // Concurrency level
        // how many browser should be started simultaneous
        concurrency: Infinity,
        webpack: {
          module: {
            loaders: [{
              test: /.js$/,
              loader: 'babel-loader',
              exclude: /node_modules/,
              query: {
                presets: ['es2015']
              }
            }]
          }
        }
      })
    }

    如上代码修改的地方:
    1. files只留下test文件。因为webpack会自动把需要的其它文件都打包进来,所以只需要留下入口文件。
    2. preprocessors也修改为test文件,并加入webpack域处理器。
    3. 加入webpack配置选项。可以自己定制配置项,但是不需要entry和output。这里加上babel-loader来编译ES6代码

    命令行运行karma start,成功了~

    但是我们再来看看 coverage/xx/index.html

    如上图测试覆盖率 不是100%;
    原因是webpack会加入一些代码,影响了代码的Coverage。如果我们引入了一些其它的库,比如jquery之类的,将源代码和库代码打包在一起后,覆盖率会更难看。
    因此我们需要安装如下插件来解决上面的问题。
    istanbul的介绍:
    istanbul是一个单元测试代码覆盖率检查工具,可以很直观地告诉我们,单元测试对代码的控制程度。

    1. webpack的Loader处理器 npm install istanbul-instrumenter-loader --save-dev
    2. 测试覆盖率显示插件 npm install karma-coverage-istanbul-reporter --save-dev

    然后我们去修改 karma.conf.js

    webpack: {
      module: {
        rules: [
          {
            test: /.js$/,
            use: {
              loader: 'babel-loader',
              options: {
                presets: ['es2015'],
                plugins: ['istanbul']
              } 
            },
            exclude: /node_modules/
          }
        ]
      }
    }

    先给babel加上插件 plugins: ['istanbul'];
    再写上 istanbul-instrumenter-loader 的配置,所以整个配置的代码变成如下:

    然后把 karma.conf.js 配置改成如下:

    webpack: {
      module: {
        rules: [
          {
            test: /.js$/,
            use: {
              loader: 'istanbul-instrumenter-loader',
              options: { esModules: true }
            },
            enforce: 'pre',
            exclude: /node_modules/
          },
          {
            test: /.js$/,
            use: {
              loader: 'babel-loader',
              options: {
                presets: ['es2015'],
                plugins: ['istanbul']
              } 
            },
            exclude: /node_modules/
          }
        ]
      }
    }

    具体可以看 https://doc.webpack-china.org/loaders/istanbul-instrumenter-loader 的API。
    因此所有的karma.conf.js 的配置代码如下:

    module.exports = function(config) {
      config.set({
    
        // base path that will be used to resolve all patterns (eg. files, exclude)
        basePath: '',
    
    
        // frameworks to use
        // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
        frameworks: ['jasmine'],
    
    
        // list of files / patterns to load in the browser
        files: [
          /* 注意:是自己添加的 */
          'test/**/*.js'
        ],
    
    
        // list of files / patterns to exclude
        exclude: [],
    
    
        // preprocess matching files before serving them to the browser
        // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
        preprocessors: {
          'test/**/*.js': ['webpack']
        },
    
    
        // test results reporter to use
        // possible values: 'dots', 'progress'
        // available reporters: https://npmjs.org/browse/keyword/karma-reporter
        reporters: ['progress', 'coverage'],
    
        /* 新增的配置项 */
        coverageReporter: {
          type: 'html',
          dir: 'coverage/'
        },
    
        // web server port
        port: 9876,
    
    
        // enable / disable colors in the output (reporters and logs)
        colors: true,
    
    
        // level of logging
        // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
        logLevel: config.LOG_INFO,
    
    
        // enable / disable watching file and executing tests whenever any file changes
        autoWatch: true,
    
    
        // start these browsers
        // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
        browsers: ['Chrome'],
    
    
        // Continuous Integration mode
        // if true, Karma captures browsers, runs the tests and exits
        singleRun: false,
    
        // Concurrency level
        // how many browser should be started simultaneous
        concurrency: Infinity,
        webpack: {
          module: {
            rules: [
              {
                test: /.js$/,
                use: {
                  loader: 'istanbul-instrumenter-loader',
                  options: { esModules: true }
                },
                enforce: 'pre',
                exclude: /node_modules/
              },
              {
                test: /.js$/,
                use: {
                  loader: 'babel-loader',
                  options: {
                    presets: ['es2015'],
                    plugins: ['istanbul']
                  } 
                },
                exclude: /node_modules/
              }
            ]
          }
        }
      })
    }

    再运行  karma start 即可。

    7. 怎么测试覆盖率
    覆盖率它有四个测量维度。
    1. 行覆盖率(line coverage): 是否每一行都执行了?
    2. 函数覆盖率(function coverage): 是否每个函数都调用了?
    3. 分支覆盖率 (branch coverage): 是否每个if代码都执行了?
    4. 语句覆盖率(statement coverage): 是否每个语句都执行了?

    karma-demo
        |---src
        |  |--index.js
        |  |--- add.js
        |--- test
        | |-- index.test.js
        | |-- add.test.js
        |
        |--- karma.conf.js

    如上demo,我们在项目中的src路径下新增add.js代码如下:

    function add (num1, num2) {
      return num1 + num2;
    }
    
    module.exports = add;

    在test/add.test.js代码如下:

    const add = require('../src/add');
    
    describe('加法运算', () => {
      it('测试简单的两个数相加', () => {
        expect(add(1, 1)).toBe(2);
      })
    });

    然后我们继续运行 karma start 后,会生成 coverage / xx/ index.html 运行结果如下:

    现在我们将add.js代码变复杂点,如果不写num2, 就默认为0,如下代码:

    function add (num1, num2) {
      if (num2 === undefined) {
        num2 = 0;
      }
      return num1 + num2;
    }
    
    module.exports = add;

    test/add.test.js 代码如下:

    const add = require('../src/add');
    
    describe('第二个测试套件', function() {
        it('第一个测试用例: 1+1 === 2', function() {
            expect(add(1)).toBe(2);
        });
    });

    我们继续karma start后,再打开 coverage下的index.html文件变为如下:

    测试结果分析:
    1个分支覆盖率(branch coverage)没有覆盖到,1个函数和1个语句被覆盖到,4行行覆盖率全部被覆盖。

    我们再继续对add.js代码进行改造。

    function add (num1, num2) {
      if (num1 === undefined) {
        num1 = 0;
      }
      if (num2 === undefined) {
        num2 = 0;
      }
      return num1 + num2;
    }
    
    module.exports = add;

    add.test.js 代码测试如下:

    const add = require('../src/add');
    describe('第二个测试套件', function() {
      it('第1个测试用例: 1+1 === 2', function() {
        expect(add(1, 1)).toBe(2);
      });
      it('第2个测试用例: 1+1 === 2', function() {
        expect(add()).toBe(0);
      });
      it('第3个测试用例: 1+1 === 2', function() {
        expect(add(1)).toBe(1);
      });
      it('第4个测试用例: 1+1 === 2', function() {
        expect(add(2, 1)).toBe(3);
      });
    });

    如上测试代码就全部覆盖到了。

    查看github上的demo

  • 相关阅读:
    Spring+MyBatis纯注解零XML整合(4)
    spring boot-mybatis全注解化(3)
    SpringBoot使用Mybatis注解进行一对多和多对多查询(2)
    Spring Boot中使用MyBatis注解配置详解(1)
    mysql 俩个时间相减后取分钟
    mysql加减时间-函数-时间加减
    十种常见排序算法(转)
    开源VS商用,IBM区块链从Hyperledger到商用平台之道 | 对话IBM高级架构师【 笔记】(转)
    java中正则表达式基本用法(转)
    《数学之美》——维特比和他维特比算法
  • 原文地址:https://www.cnblogs.com/tugenhua0707/p/8433847.html
Copyright © 2011-2022 走看看