美文网首页
Vue单元测试

Vue单元测试

作者: CodeMT | 来源:发表于2023-01-12 11:35 被阅读0次

    背景

    从大的方面说前端应用本质上是一种特殊的GUI应用,GUI软件测试传送门。它的测试用例、覆盖率统计、测试方法等等都与API测试有着很大的不同。因此,在这个大前提下,我们来看前端测试的特殊性。

    对于前端来说,业务基础代码比较稳定,比如通用组件、通用的工具类和通用的业务组件等,可以针对这些建立复杂一些的API和GUI测试用例来保证质量。剩下的部分不是很稳定,每天都在迭代,针对他们维护case的成本非常高。业界通用的方法其实就是我们看到的一般测试人员的点点。

    我们如何保证基础代码的稳定及可靠呢?这就是我们要讨论的单元测试以及测试的覆盖率

    单元测试

    单元测试框架选择mocha,断言库选择chai,关于mochachai的介绍以及如何安装我就不科普啦!我们介绍下他俩的用法。

    基本测试用例的编写

    mocha的用法

    describe('一组测试的描述', function() {
      before(function() {
        // 所有的测试用例执行前的钩子函数
      });
    
      after(function() {
        // 所有的测试用例执行后的钩子函数
      });
    
      beforeEach(function() {
        // 所有的测试用例执行前每次都会执行的钩子函数
      });
    
      afterEach(function() {
        // 所有的测试用例执行后每次都会执行的钩子函数
      });
      it('具体的测试用例',function () {
    
      })
    }
    

    chai的用法

    var expect = require('chai').expect;
    // 相等或不相等
    expect(4 + 5).to.be.equal(9);
    expect(4 + 5).to.be.not.equal(10);
    expect(foo).to.be.deep.equal({ bar: 'baz' });
    
    // 布尔值为true
    expect('everthing').to.be.ok;
    expect(false).to.not.be.ok;
    
    // typeof
    expect('test').to.be.a('string');
    expect({ foo: 'bar' }).to.be.an('object');
    expect(foo).to.be.an.instanceof(Foo);
    
    // include
    expect([1,2,3]).to.include(2);
    expect('foobar').to.contain('foo');
    expect({ foo: 'bar', hello: 'universe' }).to.include.keys('foo');
    
    // empty
    expect([]).to.be.empty;
    expect('').to.be.empty;
    expect({}).to.be.empty;
    
    // match
    expect('foobar').to.match(/^foo/);
    

    下面我们通过简单的一个加法函数来举例说明

    // 假设文件名为add.js
    function add (a,b) {
       return a + b 
    }
    
    // 测试文件
    const add = require('../src/add.js')
    const expect = require('chai').expect
    describe('add', function() {
      it('返回结果是数字', () => {
        expect(add(2,3)).to.be.a('number');
      })
      it('4+4返回结果是8', () => {
        expect(add(4,4)).to.equal(8)
      })
    });
    

    异步测试编写

    其实异步是我们日常比较常见的场景,如果要测试异步函数,我们要传入的函数需要带一个参数,通常命名为done,测试异步函数需要在函数内部手动调用done()表示测试成功,done(err)表示测试出错。

    it('异步测试', function (done) {
      fs.readFile('path', function (err, data) {
        if (err) {
          done(err);
        } else {
          done();
        }
      });
    });
    // promise
    it('promise的测试', function(done) {
      return new Promise(function(resolve) {
        assert.ok(true);
        resolve();
      }).then(done);
    });
    // async await
    it('async await', async function() {
      const users = await db.find({type: 'User'});
      users.should.have.length(3);
    });
    

    接口测试

    SuperTest 是 SuperAgent 的一个扩展,一个轻量级的 HTTP AJAX 请求库。SuperTest 提供了用于测试 node.js API 终端响应的高阶抽象,使得断言便于理解。

    describe('用户信息接口测试', function() {   
      it('/device api test',function(done) {
        request(app)
          .post('api/v2/user/create')
          .set('Content-Type','application/json')
          .set('Authorization', 'Bearer ' + accessToken)
          .send({
            user: username,
            password: password
          })
          .expect(200) //断言希望得到返回http状态码
          .end(function(err, res) {
            console.info(res.body); //得到返回我们可以用2.2中的断言对返回结果进行判断。
            done();
          });
      });
    });
    

    vue中测试用例的编写

    我们就以一个button组件为例来说明吧。

    const expect = require('chai').expect
    import Vue from 'vue'
    import Button from '../src/button'
    describe('Button', () => {
      it('确定按钮存在', () => {
        const Constructor = Vue.extend(Button)
        const vm = new Constructor({
          propsData: {
            type: 'primary'
          }
        }).$mount()
        let buttonElm = vm.$el
        expect(buttonElm.classList.contains('el-button-primary')).to.be.true;
        vm.$destory()
      })
    
      it('可以设置icon', () => {
        const Constructor = Vue.extend(Button)
        const vm = new Constructor({
          propsData: {
            icon: 'settings'
          }
        }).$mount()
        let buttonElm = vm.$el
        expect(buttonElm.querySelector('.el-icon-settings')).to.be.ok;
          vm.$destory()
        })
    
      it('可以设置loading.', () => {
        const Constructor = Vue.extend(Button)
        const vm = new Constructor({
          propsData: {
            icon: 'settings',
            loading: true
          }
        }).$mount()
        let buttonElm = vm.$el
        expect(buttonElm.querySelector('.el-icon.loading')).to.be.ok
        vm.$destroy()
      })
    
      it('点击 button 触发 click 事件', () => {
        let result;
        vm = new Vue({
          template: `<el-button @click="handleClick"></el-button`,
          methods: {
            handleClick (evt) {
              result = evt;
            }
          }
        }).$mount()
        vm.$el.click()
        setTimeout(() => {
          expect(result).to.exist
          done()
        }, 20)
      })
    })
    

    测试环境的搭建

    前面聊了如何做单元测试,以及介绍了如何写测试用例,如何根据我们的前端项目搭建一套测试框架呢?我们列出所要用到的技术。

    • karma 该工具可用于测试所有主流Web浏览器,也可集成到CI(Continuous integration)工具,也可和其他代码编辑器一起使用。
    • mocha 前端测试框架,支持node和浏览器端,断言库自由化,注意mocha只提供了测试套件,具体的断言工具还需要在挑选,这里我们选用chai
    • chai 断言库,提供了should,expect,assert多种断言。
    • istanbul javascript代码覆盖率工具,用来导出代码覆盖率数据。

    需要安装的包名如下:

    • karma:提供测试所需的浏览器环境、监测代码改变自动重测、整合持续集成等功能
    • mocha:测试框架,运行测试
    • chai:断言库,提供多种断言,与测试框架配合使用
    • sinon:测试辅助工具,提供 spy、stub、mock 三种测试手段,帮助捏造特定场景
    • karma-webpack:karma 中的 webpack 插件
    • karma-mocha:karma 中的 mocha 插件
    • karma-sinon-chai:karma 中的 sinon-chai 插件
    • sinon-chai:karma 中的 chai 插件
    • karma-sourcemap-loader:karma 中的 sourcemap 插件
    • karma-chrome-launcher:karma 中的模拟chrom插件
    • karma-spec-reporter:在终端输出测试结果
    • babel-plugin-istanbul:babel插件,es6代码产生instanbul覆盖率
    • karma-coverage:Karma插件,生成代码覆盖率

    目录结构

    vue-template-test
      src
        components
          Button.vue
        utils
          utils.js
        test
          unit
            specs
              button.spec.js
            index.js
            karma.conf.js
      .babelrc
      package.json 在package.json配置测试运行的环境
    
    "scripts": {
      "BABEL_ENV=test karma start test/unit/karma.conf.js --single-run"
    }
    

    karma.conf.js配置详解

    module.exports = function(config) {
      const configuration = {
        // 设置默认打开的浏览器
        browsers: ['ChromeHeadless'],
        // 选择测试套件库和断言
        frameworks: ['mocha', 'sinon-chai'],
        // 设置测试覆盖率输出插件
        reporters: ['spec', 'coverage'],
        // 测试入口文件
        files: ['./index.js'],
        // 用webpack解析,同时显示测试文件路径
        preprocessors: {
          './index.js': ['webpack', 'sourcemap']
        },
        webpack: {
          module: {
            loaders: [
              {
                test: /\.js$/,
                loader: 'babel-loader',
                exclude: /node_modules/
              },
              {
                test: /\.vue$/,
                loaders: [{
                  loader: 'vue-loader',
                }]
              },
            ]
          },
          // 是否打印webpack打包信息
          webpackMiddleware: {
            noInfo: true
          },
          // karma-coverage配置,配置测试覆盖率的输出目录及格式
          coverageReporter: {
            dir: './coverage',
            reporters: [
              { type: 'lcov', subdir: '.' },
              { type: 'text-summary' }
            ]
          },
          client: {
            mocha: {
              timeout: 4000
            }
          }
        }
      };
      config.set(configuration);
    };
    

    入口文件index.js的配置

    // load 所有的测试用例文件
    const testsContext = require.context('./specs', true, /\.spec$/)
    testsContext.keys().forEach(testsContext)
    
    // load 所有的资源文件,及src目录下的除了main.js文件的所有文件, 即要覆盖的所有代码
    const srcContext = require.context('../../src', true, /^\.\/(?!main(\.js)?$)/)
    srcContext.keys().forEach(srcContext)
    

    相关文章

      网友评论

          本文标题:Vue单元测试

          本文链接:https://www.haomeiwen.com/subject/mzmrortx.html