Vue的单元测试探索(二)
作者:小冉 发布时间:[ 2017/6/6 15:13:17 ] 推荐标签:软件测试 单元测试
对于 .vue 单文件组件的单元测试,一般的方法使用 Karma,配合 Webpack,headless browser,再加上你喜欢的测试框架(mocha、ava、jasmime、node-tap)。这个过程配置繁琐,而且需要 Webpack 把整个项目编译打包,再在 browser 上跑测试,执行过程也很慢。
而我期望中的测试方法应该像 JS 单元测试一样,构造组件实例,调用组件方法或属性进行测试,比如这样一个组件:
// a.vue
<template>
<div>
<button @click="minus" class="minus">-</button>
<input :value="result" disabled />
<button @click="plus" class="plus">+</button>
</div>
</template>
<script>
export default {
data() {
return { result: 0 }
},
methods: {
minus() {
this.result--;
},
plus() {
this.result++;
}
}
};
</script>
<style></style>
期望的测试方法是这样的:
const test = require('ava');
const Vue = require('vue');
let a = require('a.vue');
let vm = new Vue(a);?
test(t => {
t.is(vm.result, 0);
vm.plus();
vm.minus();
...
});
那么如何实现?
直接引用 .vue 文件会报错,因为 .vue 文件一般的结构包括 template、script、style 三部分,文件内容并不符合 JS 语法。
const a = require('a.vue');
<template>
SyntaxError: Unexpected token <
第一个问题,如何引入 .vue 文件?
在这之前,需要先了解一下 Node 的模块加载机制。
首先是 Module 构造函数,用于生成模块实例,其中模块 id 是文件路径,作为模块索引。
// Module.js
// 模块构造函数
function Module(id, parent) {
this.id = id; // filename,路径,作为索引
this.exports = {};
...
}
接着是 Module 两个比较重要的属性,后面会讲到,一个是模块缓存,缓存已加载的模块;另一个是模块扩展,预定义不同文件类型的加载方式。
Module._cache = Object.create(null); // 模块缓存
Module._extensions = Object.create(null);
后是 Module 加载模块用到的关键方法:
Module.prototype._load
Module.prototype._compile
Module.prototype.require
现在来看模块加载过程
1. Module.prototype._load 过程
Module.prototype._load = (request, parent, isMain) => {
1. 检查 Module._cache 中有没有缓存;
2. 如果没有, new Module(), 并缓存;
3. 执行一些检查;
4. 根据文件后缀类型, 执行 Module. _extensions 中预设方法;
5. 预设方法会读取文件, 执行 Module.prototype._compile;
6. 返回 module.exports
};
2. Module.prototype._compile 过程
Module.prototype._compile = (content, filename) => {
1. 构造 require 方法
? // Module.prototype.require 是对 Module.prototype._load 简单包装
require = Module.prototype.require;
2. 为 require 方法附加属性:
...
require.cache = Module._cache;?
require.extensions = Module._extensions;
3. 将模块包装为一个包装方法?
(function(exports, require, module, __filename, __dirname) {
// 模块内容
})
4. 运行方法
};
总结一下,基本的过程是这样的:
1. 加载模块,检查缓存;
Module.prototype._load();
2. 根据预定义文件后缀处理方法,编译模块,将模块打包为包装方法,并传入参数(require, module, exports…)
3. 执行包装方法,加载模块内的引用
(function(exports, require, module, __filename, __dirname) {
// 模块内容
require(sth) -> Module.prototype._load()
});
4. 返回模块 module.exports
回到第一个问题,怎么引入 .vue 文件?方法是在 require.extensions 中增加 .vue 文件的处理方法。
require.extensions['.vue'] = function(module, filename) {
var file = fs.readFileSync(filename, 'utf8');
// 解析 .vue 文件内容,输出符合 JS 语法的字符串
var script = extract(file);
module._compile(script, filename);
};
第二个问题,怎么解析 .vue 文件,输出符合 JS 语法的字符串?
.vue 单文件组件中,测试需要的是 template 和 script 两个部分,期望输出的是带 template 属性的 JS 对象字符串。解析方法可以使用正则,匹配标签,抽出 template 和 script 的内容,再进行拼接。
这里有个小问题,template 内字符串抽出来了以后,怎么加到 JS 对象中?其实很简单,模块输出的内容本身是 JS 对象,给这个对象加一个 template 属性可以了:
var scriptStr = "module.exports = {
data() {
return {}
},
methods: ...
};"
var templateStr = "module.exports.template = " + JSON.stringify(templateStr) + ";"
var content = scriptStr + templateStr;
其他可能遇到的问题:
1. ES6 module,目前 Node 并没有实现 ES6 module,而代码中可能已经用上了,这个问题可以用 babel 转换。
babel.transform(js, babelrc).code;
2. Vue 引入,因为需要编译 template 部分,所以测试时需要引入完整版 Vue。
const Vue = require('vue/dist/vue.common.js');
3. Webpack resolve.alias,一般配置 Webpack 时会把几个常用目录做 alias 配置,而现在不经过 Webpack 预处理,所以 alias 相关路径引用会出现问题,可以使用 module-alias 这个库,在引入测试模块前修改关键路径,或者直接改写一下 Module._resolveFilename 方法。
4. DOM 操作,可以使用 jsdom-global 简单模拟 DOM 结构和事件。
require('jsdom-global')();
问题解决,测试方法:
require('vue-tester'); // 使用上述方法写的工具,处理 .vue 文件?require(‘jsdom-global’)();?
const Vue = require('vue/dist/vue.common.js');?
const ava = require('ava');??
const a = require('a.vue').default;
?let vm = new Vue(a);
vm.$mount();
?test(t => {?
t.is(vm.result, 0);
vm.$el.querySelector('button.plus').click();
t.is(vm.result, 1);
vm.$el.querySelector('button.minus').click();
t.is(vm.result, 0);?});
});
小结
根据 Node 模块引用原理,预定义 .vue 文件读取规则,再通过正则方式将 .vue 文件内容解析为符合 JS 语法的字符串,拿到解析后的组件对象进行属性、方法等测试。这种方法已经可以完成一般 .vue 组件的单元测试,而且更简单、更快速。
相关推荐
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11