单测的意义就在于当你重构代码的时候,能够避免你的新代码不出现以前解决的问题,虽然它很重要,但是工作中总是会把它忽视,因为太耗时间了。但我们要搭建的函数库大概当然是我们积累的精华代码,它值得我们花点功夫编写单测。如果有没写过单测的同学,建议先翻翻这篇测试框架Mocha实例教程-阮一峰
所幸 vue-cli 生成的项目已经帮我们集成好了单测所需的包以及一些配置文件,我们可以直接启动 npm run test 就可以跑起测试命令。
package.json
这是和测试相关的三条命令,其中我加入了一条unit:watch命令,同上面比知识去掉 --single-run参数,编写单测的时候运行这条命令能够监听文件的变动而再次运行单测,更加省力些。
- ...
- "scripts": {
- ...
- "unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run",
- "unit:watch": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js",
- "test": "npm run unit",
- ...
- },
- ...
接着删除test/unit/specs/HelloWorld.spec.js,并添加一个test/unit/specs/formatTime.spec.js
test/unit/specs/formatTime.spec.js
- import Vue from 'vue';
- import formatTime from '~/formatTime.js';
- Vue.use(formatTime);
- describe('formatTime',
- function() {
- // 新建一个vm对象用于实验vue.filter过滤器是否生效的
- let vm = new Vue({
- template: ` < p > {
- {
- testDate | formatTime('YYYY-MM-DD')
- }
- } < /p>
- `,
- data() {
- return {
- testDate: 1490774006864
- };
- }
- }).$mount(null);
- it('Vue.formatTime', () => {
- / / 2017年3月29日15点53分26秒let date = vm.$data.testDate;
- let dayFormat = Vue.formatTime(date, 'YYYY-MM-DD');
- let secondFormat = Vue.formatTime(date, 'YYYY-MM-DD hh:mm:ss');
- expect(dayFormat).to.be.equal('2017-03-29');
- expect(secondFormat).to.be.equal('2017-03-29 15:53:26');
- date = 123;
- expect(Vue.formatTime(date, 'YYYY-MM-DD')).to.be.equal('1970-01-01');
- date = '2017年3月29日';
- expect(Vue.formatTime(date, 'YYYY-MM-DD')).to.be.equal('2017年3月29日');
- date = '';
- expect(Vue.formatTime(date, 'YYYY-MM-DD')).to.be.equal('');
- date = null;
- expect(Vue.formatTime(date, 'YYYY-MM-DD')).to.be.equal('');
- date = void 0;
- expect(Vue.formatTime(date, 'YYYY-MM-DD')).to.be.equal('');
- });
- it('Vue.protoType.formatTime', () = >{
- // 2017年3月29日15点53分26秒
- let date = vm.$data.testDate;
- let dayFormat = vm.$formatTime(date, 'YYYY-MM-DD');
- let secondFormat = vm.$formatTime(date, 'YYYY-MM-DD hh:mm:ss');
- expect(dayFormat).to.be.equal('2017-03-29');
- expect(secondFormat).to.be.equal('2017-03-29 15:53:26');
- });
- it('Vue.filter: formatTime', () = >{
- expect(vm.$el.innerText).to.be.equal('2017-03-29');
- });
- // 遍历完所有formatTime的单测后删除vm
- afterEach(() = >{
- vm.$el && vm.$el.parentNode && vm.$el.parentNode.removeChild(vm.$el);
- });
- });
由于暴露的三种调用方式都一样,于是我只给第一种方式加详细内容。我测试了一些不合法的输入,针对字符串希望能返回本字符串,null 和 undefined返回空字符串。不能被转化为正常时间的数字 123,因为
为
- new Date(123)
。
- Thu Jan 01 1970 08:00:00 GMT+0800 (CST)
额,我就贴上关键信息吧
- $ npm run test
- > tt-utils@1.0.3 unit /Users/everlose/workspace/github/tt-utils
- > cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run
- formatTime
- ✓ Vue.formatTime
- ✓ Vue.protoType.formatTime
- ✓ Vue.filter: formatTime
- TOTAL: 3 SUCCESS
- =============================== Coverage summary ===============================
- Statements : 90% ( 18/20 )
- Branches : 73.33% ( 11/15 )
- Functions : 100% ( 2/2 )
- Lines : 88.89% ( 16/18 )
- ================================================================================
单测运行成功,并且所有单测结果正确了。
注意尾部的信息 Coverage summary,这是你编写的单测代码覆盖率的信息,依照上面显示的我们编写的单测只达到73.33%的分支条件,最好能想办法达到 90% 以上,你的源码才会可靠。
找到源码 src/formatTime.js ,我发现似乎不能正常转化为date的这个分支我没有测试到位
- var date = new Date(value);
- if (Number.isNaN(date.getTime())) {
- return value;
- }
于是给单测 formatTime.spec.js 加入一个判断
- // ...
- date = 149077400686412312313;
- expect(Vue.formatTime(date, 'YYYY-MM-DD')).to.be.equal(149077400686412300000);
- // ...
运行后发现
- =============================== Coverage summary ===============================
- Statements : 95% ( 19/20 )
- Branches : 80% ( 12/15 )
- Functions : 100% ( 2/2 )
- Lines : 94.44% ( 17/18 )
在分析源码得知其实还可以传入毫秒数,我的单测里缺没有传
- var o = {
- // ...
- 'S': date.getMilliseconds() // millisecond
- };
于是单测里加上
- // ...
- let secondFormat = Vue.formatTime(date, 'YYYY-MM-DD hh:mm:ss:S');
- expect(secondFormat).to.be.equal('2017-03-29 15:53:26:864');
- // ...
运行后发现
- =============================== Coverage summary ===============================
- Statements : 95% ( 19/20 )
- Branches : 86.67% ( 13/15 )
- Functions : 100% ( 2/2 )
- Lines : 94.44% ( 17/18 )
额,暂时我也不知道该怎么把 Branches 再提升一下了,留个坑后面回来补。
大家或许留意到了 formatTime.spec.js 最后一段
- // 遍历完所有formatTime的单测后删除vm
- afterEach(() = >{
- vm.$el && vm.$el.parentNode && vm.$el.parentNode.removeChild(vm.$el);
- });
每次增删一个 vm 都这么麻烦,那是该抽象出来了,所以我这里致敬了一下element ui 源码。
追加一份 test/unit/utils.js
- import Vue from 'vue';
- let id = 0;
- const createElm = function() {
- const elm = document.createElement('div');
- id += 1;
- elm.id = `app$ {
- id
- }`;
- document.body.appendChild(elm);
- return elm;
- };
- /**
- * 回收 vm
- * @param {Object} vm
- */
- exports.destroyVM = function(vm) {
- vm.$el && vm.$el.parentNode && vm.$el.parentNode.removeChild(vm.$el);
- };
- /**
- * 创建一个 Vue 的实例对象
- * @param {Object|String} Compo 组件配置,可直接传 template
- * @param {Boolean=false} mounted 是否添加到 DOM 上
- * @return {Object} vm
- */
- exports.createVue = function(Compo, mounted = false) {
- if (Object.prototype.toString.call(Compo) === '[object String]') {
- Compo = {
- template: Compo
- };
- }
- return new Vue(Compo).$mount(mounted === false ? null: createElm());
- };
接着修改 test/unit/spec/formatTime.js
- // ...
- import { createVue, destroyVM } from '../utils';
- //...
- describe('formatTime', function () {
- let vm = createVue({
- template: `
- <p>{{ testDate | formatTime('YYYY-MM-DD') }}</p>
- `,
- data() {
- return {
- testDate: 1490774006864
- };
- }
- }, true);
- // ...
- afterEach(() => {
- destroyVM(vm);
- });
- }
看懵逼了么?正是因为编写单测的过程非常繁琐,还得思考自己以前编写的时候压根没有想到的分支条件,可能写单测的时间还会多于编写源码的时间。
本篇所有代码在 tag v0.0.4 中, 给你参考。
上一篇:npm包发布篇
下一篇:md示例文档编写篇
来源: https://juejin.im/entry/5a1434ab6fb9a0450a66faa3