Vue单元测试:unit testing best practices

预备知识

Vue + VS Code + helloworld
HTML & JavaScript & CSS语法 - w3cschool
ECMAScript 6入门 - w3school
ECMAScript 6入门 - 阮一峰
Vue2 官方教程
Vuex 官方教程
sass 教程

ECMAScript 6(以下简称ES6)是JavaScript语言的下一代标准。因为当前版本的ES6是在2015年发布的,所以又称ECMAScript 2015。 也就是说,ES6就是ES2015

Javascript和ECMAScript并不一样。ECMAScript是一种脚本在语法和语义上的标准,主要包括:语法、类型、语句、关键字、保留字、操作符、对象等, 跟浏览器没任何关系。Javascript是基于ECMAScript标准实现的。Javascript不仅仅包括ECMAScript,它还包含了DOM和BOM; DOM(Document Object Model)是HTML和XML的应用程序接口(API),W3C的标准即事为它而制定。BOM(Browser Object Model)是提供与浏览器进行交互的方法和接口,由于不同的浏览器实现方法不同,从而表现也可能不同。且BOM的核心是window对象,而window又是一个全局对象,这就意味着网页中定义的任何对象、变量、函数都是以window作为全局对象的。并且可以这么说,BOM是包含了DOM。

jest.mock

待测JS类:

// MyLodash.js
import _ from 'lodash';

export default class MyLodash {
    constructor(name) {
        this.name = name;
    }

    getName() {
        const users = [
            { user: 'barney', active: false },
            { user: 'fred', active: false },
            { user: 'pebbles', active: true },
        ];

        return _.findIndex(users, function(o) { return o.user === 'barney'; });
    }
    getName2() {
        return 'bob2';
    }
}

如果要MOCK掉_.findIndex,可以使用如下方法:

// MyLodash.spec.js
import MyLodash from '@/pilot/MyLodash';

jest.mock('lodash', () => ({
    findIndex: () => {return 2},
}));

describe('MyLodash', () => {
    test('test1', () => {
        const ml = new MyLodash('a');
        #console.log输出的值是mock的值2
        console.log(ml.getName());
    });

});

但上述代码无法判断mock的findIndex调用了多少次。更新如下:

import MyLodash from '@/pilot/MyLodash';
import _ from 'lodash';

const findIndexMock = jest.fn();
findIndexMock.mockReturnValue('222');
jest.mock('lodash');
_.findIndex = findIndexMock;

describe('MyLodash', () => {
    test('test1', () => {
        const ml = new MyLodash('a');
        #console.log输出的值是mock的值222
        console.log(ml.getName());
        expect(findIndexMock).toHaveBeenCalledTimes(1);
    });

});

window.URL.createObjectURL的mock

in jest, global is window. So if you want to use the window object, don't use global.window — just use global.

在测试代码里可以这样mock

describe('ApiManager', () => {

    test('mock global window', () => {
        global.URL.createObjectURL = jest.fn();
        apiManager._downloadFile('a','testme', 'html');
        expect(global.URL.createObjectURL).toHaveBeenCalledTimes(1);
    });
});

单元测试的时候获取某些类的值

如果有些类或者对象很复杂,可以打开网页,在网页里找到这个值复制下来放到单元测试代码里,比自己创建这个对象快多了。

试了chrome和firefox,都可以的。

在复制拷贝的时候JONS.stringfy会提示cyclic object value,这时候可以通过如下方式解决,编写一个简单的函数,其中的circularReference就是提示cyclic object value的那个对象:

const getCircularReplacer = () => {
  const seen = new WeakSet();
  return (key, value) => {
    if (typeof value === "object" && value !== null) {
      if (seen.has(value)) {
        return;
      }
      seen.add(value);
    }
    return value;
  };
};

JSON.stringify(circularReference, getCircularReplacer());
// {"otherData":123}

之后可以通过alert弹框展出出这个json对象。在浏览器里的console输出的JSON对象里面包含了大量的反斜杠。

mount和shallowmount

console.log(wrapper.html());打印的信息会有所不同
对于同样的如下源代码:





shallowmount的输出如下

Yes No

mount的输出如下

区别就是是否stub子组件

调试技巧

可以在源代码里加些console.log(), 在运行单元测试的时候查看是否会触发源代码里相应的console.log().

Reference

https://babeljs.io/docs/en/learn
https://www.manning.com/books/testing-vue-js-applications
https://alexjover.com/blog/test-methods-and-mock-dependencies-in-vue-js-with-jest/
https://vuejs.org/v2/cookbook/unit-testing-vue-components.html
https://engineering.doximity.com/articles/five-traps-to-avoid-while-unit-testing-vue-js
https://lmiller1990.github.io/vue-testing-handbook/computed-properties.html#testing-by-rendering-the-value
https://vue-test-utils.vuejs.org/
https://jestjs.io/docs/en/next/getting-started.html
https://github.com/eddyerburgh/avoriaz
https://github.com/alexjoverm/vue-testing-series
https://markus.oberlehner.net/blog/testing-vuex-powered-vue-components-with-jest/
https://alexjover.com/blog/test-deeply-rendered-vue-js-components-in-jest/

你可能感兴趣的:(Vue单元测试:unit testing best practices)