单元测试

单元测试

1.BDD&&TDD

  1. BDD( Behavior-Driven-Development ):行为驱动开发 , 主要是设计一些行为,进行对代码的测试

    1
    2
    3
    4
    5
    describe('Button',()=>{  //Mocha语法,下面会介绍Mocha
    it('存在',()=>{
    //测试
    })
    })

    这样的写法,我们甚至可以读出来, 描述 Button , 存在.

  2. TDD(Test-Driven-Development): 测试驱动开发,自动化单元测试来推动软件设计并强制依赖关系解耦的技术,可以单独进行.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    const expect = chai.expect
    {
    const Construstor = Vue.extend(Button);
    const vm = new Construstor({
    propsData:{
    icon : 'setting'
    }
    });
    vm.$mount();
    let useElement = vm.$el.querySelector('use');
    let href = useElement.getAttribute('xlink:href');
    expect(href).to.eq('#i-setting'); // 期望 href === '#i-setting' chai.js语法
    vm.$el.remove();
    vm.$destroy();
    }

2.chai.js

chai.js 是专门用来执行单元测试的一个库 , 有 assert(断言) , should(应该) , expect(期望) 三种语法.

expect常用语法:

1
2
3
4
5
const expect = chai.expect;
expect(foo).to.be.a('string');
expect(foo).to.equal('mount');
expect(foo).to.have.lengthOf(3);
expect(foo).to.have.property('flavors').with.lengthOf(3);

语法很简单,详情查阅文档

3.使用 Karma + Mocha做单元测试

1.工具

  1. Karma(卡玛)是一个测试运行器,它可以呼起浏览器,加载测试脚本,然后运行测试用例

  2. Mocha(摩卡)是一个单元测试框架库,它用来写测试用例

  3. Sinon(西农) 是一个 spy/mock/stub 库.

    安装工具

    npm i -D karma karma-chrome-launcher karma-mocha karma-sinon-chai mocha sinon sinon-chai karma-chai karma-chai-spies

2.步骤

  1. 创建Karma配置

    里面有两个关键部位已经标出

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    // 新建 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: ['mocha', 'sinon-chai'],
    client: {
    chai: {
    includeStack: true
    }
    },


    // list of files / patterns to load in the browser
    files: [
    'dist/**/*.test.js',
    'dist/**/*.test.css'
    ],


    // 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 results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress'],


    // 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: ['ChromeHeadless'],


    // 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
    })
    }
    1. 创建 test/button.test.js 文件

      • $mount()

        如果 Vue 实例在实例化时没有收到 el 选项,则它处于“未挂载”状态,没有关联的 DOM 元素。可以使用 vm.$mount() 手动地挂载一个未挂载的实例。

        如果没有提供 elementOrSelector 参数,模板将被渲染为文档之外的的元素,并且你必须使用原生 DOM API 把它插入文档中。

        这个方法返回实例自身,因而可以链式调用其它实例方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
const expect = chai.expect;
import Vue from 'vue'
import Button from '../src/button'

Vue.config.productionTip = false
Vue.config.devtools = false

describe('Button', () => {
it('存在.', () => {
expect(Button).to.be.ok
})
it('可以设置icon.', () => {
//使用extend方法,将Button对象构造成一个构造函数
const Constructor = Vue.extend(Button)
//然后通过构造函数实例化Button
const vm = new Constructor({
propsData: {
icon: 'settings'
}
}).$mount() //使用$mount()挂载,详情看上面
const useElement = vm.$el.querySelector('use')
expect(useElement.getAttribute('xlink:href')).to.equal('#i-settings')
vm.$destroy()
})
it('可以设置loading.', () => {
const Constructor = Vue.extend(Button)
const vm = new Constructor({
propsData: {
icon: 'settings',
loading: true
}
}).$mount()
const useElements = vm.$el.querySelectorAll('use')
expect(useElements.length).to.equal(1)
expect(useElements[0].getAttribute('xlink:href')).to.equal('#i-loading')
vm.$destroy()
})
it('icon 默认的 order 是 1', () => {
const div = document.createElement('div')
document.body.appendChild(div)
const Constructor = Vue.extend(Button)
const vm = new Constructor({
propsData: {
icon: 'settings',
}
}).$mount(div)
const icon = vm.$el.querySelector('svg')
expect(getComputedStyle(icon).order).to.eq('1')
vm.$el.remove()
vm.$destroy()
})
it('设置 iconPosition 可以改变 order', () => {
const div = document.createElement('div')
document.body.appendChild(div)
const Constructor = Vue.extend(Button)
const vm = new Constructor({
propsData: {
icon: 'settings',
iconPosition: 'right'
}
}).$mount(div)
const icon = vm.$el.querySelector('svg')
expect(getComputedStyle(icon).order).to.eq('2')
vm.$el.remove()
vm.$destroy()
})
it('点击 button 触发 click 事件', () => {
const Constructor = Vue.extend(Button)
const vm = new Constructor({
propsData: {
icon: 'settings',
}
}).$mount()

const callback = sinon.fake(); //使用间谍函数,捕获是否进行了回调
vm.$on('click', callback) //
vm.$el.click()
expect(callback).to.have.been.called //

})
})

3.创建测试脚本

在package.json里找到 scripts

1
2
3
4
"scripts": {
"test": "parcel build test/* --no-minify --no-cache && karma start --single- run",
"dev-test": "parcel watch test/* --no-cache & karma start",
},

解释一下

  • parcel build使用parcel 打包
  • test/* test文件夹下面的所有文件
  • --no-minify 不懂,不加会出错,加上
  • --no-cache 不从缓存;里运行,有缓存也容易出错
  • karma start karma启动
  • --single-run 只运行一次
  • parcel watch parcel一直盯着,有改动就打包
  • karma strat 后面没加别的,一直运行

    4.运行测试脚本

npm run test

npm run dev-test

4.持续测试

​ 使用 Travis Ci 来进行持续测试 , 就是我们把测试跑到他的服务器上,每提交一次仓库 ,就执行一次测试.

  1. 在目录中添加 .travis.yml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    language: node_js   //指定语言 : nodejs
    node_js: //指定版本,可以做多版本测试哦,很强很牛逼
    - "8"
    - "9"
    - "10"
    addons:
    chrome: stable //使用chrome测试
    sudo: required // 需要管理员权限
    before_script: //不懂,不加可能有BUG
    - "sudo chown root /opt/google/chrome/chrome-sandbox"
    - "sudo chmod 4755 /opt/google/chrome/chrome-sandbox"
  2. 将代码提交到GitHub

  3. Travis 上使用 GITHub登录,然后将你要测试的项目添加进去

  4. 等着就完事儿了