前言#
由于需要调试一个typescript的包,而这个包中带有单元测试集,可以基于这个测试集快速上手。但是我之前并未接触过这块,所以学习中写个文章加深自己印象,后面再学ts-jest。
官方文档#
Getting Started · Jestjestjs.io/docs/getting-started
正文#
上来先安装并创建两个文件index.js、index.test.js
npm init --yes
npm install jest --save-dev
echo > index.js
echo > index.test.jsindex.js文件中导出一个sum方法
function sum (a, b) {
return a + b
}
module.exports = {
sum
}
index.test.js文件中导入sum,并写下测试代码
const { sum } = require('./index')
test('add 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3)
}) 然后将package.json中的scripts加上
"scripts": {
"test": "jest"
},然后直接npm run test

一个非常简单的例子就完成啦!
这里test函数就是一个测试元接收两个参数,第一个作为key值,第二个则是回调,其中回调用于执行你的期望函数。expect方法接收一个数据,返回一个对象,而toBe接收的参数是匹配项,如果匹配符合预期,这个测试元就通过了。
来看下expect到底返回了个什么。

由于量太大了,所以这里只展示一小部分,不过大家应该也猜出来了。这个对象是一个集合了所有测试方法的对象,倒不如叫它实例更合适点。
测试异步场景#
由于jest并不知道你的异步会在什么时候完成,所以你得将整个异步方法return给它,让它去等待异步的完成。
index.js文件中新增testAsync方法
function testAsync () {
return new Promise((resolve, reject) => {
resolve(123)
})
}然后在index.test.js文件中新增
test('test-async', () => {
return testAsync().then(res => {
expect(res).toBe(123)
})
}); 然后终端执行npm run test

正常运行。
如果你用的是async await,那么可以不用返回,直接在回调的前面加上async,请求用await即可。
test('test-async', async () => {
const res = await testAsync();
expect(res).toBe(123)
}); 另外需要注意的一点是reject失败的场景,这也是需要你自己去写的场景。
其中如果你需要promise返回一个reject的场景,你需要使用catch方法,在return之前加上expect.assertions(1);用来声明你需要这个promise报错。
test('test-async-fail', () => {
expect.assertions(1);
return testAsync().catch(e => expect(e).toBe(666))
})当然,你也可以使用没有promise之前常用的方式,通过回调将代码注入到异步场景中。但需要配合官方提供的done方法以及你这个异步方法支持回调方式,done方法来自回调,以参数的形式,在完成匹配后执行done方法通知jest完成该测试。这里就不多赘述,直接看官方例子。能async await还是尽量async await。
test('the data is peanut butter', done => {
function callback(error, data) {
if (error) {
done(error);
return;
}
try {
expect(data).toBe('peanut butter');
done();
} catch (error) {
done(error);
}
}
fetchData(callback);
});还有一些特殊用法,比如.resolve这种这里就不说了,感兴趣的大佬可自行去看下。
Testing Asynchronous Code · Jestjestjs.io/docs/asynchronous
测试client代码#
目前配置仅能运行在服务端或者测试一些纯函数,但如果我们要测试的东西涉及到window、dom这些就不适用了。幸运的是官方提供了对client的测试功能,但也并不是真的浏览器环境,只是基于js-dom模拟出来的,你的html将变成虚拟dom的形式。
我们先安装对应的依赖
npm install -D jest-environment-jsdom如果这个时候你在test文件中console.log(window),这个时候你会发现不是undefined。
然后在根目录创建一个jest.config.js文件,然后复制以下内容到文件内。
module.exports = {
// Indicates which provider should be used to instrument code for coverage
coverageProvider: "v8",
// The test environment that will be used for testing
testEnvironment: "jsdom",
};或者你已经全局安装了jest,那就可以直接在终端输入jest --init。具体流程请点击目录中全局安装jest脚手架这个。然后在对话中选择environment: js-dom(browser-like)。
接着我们创建client.test.js和client.js两个文件
如果要操作dom,那么我们的html模板就得放到document中。
我们将template放在client.js文件中,模拟我们的html
const template = `
<div class="test">123</div>
<button id="btn" class="btn"></button>
`
module.exports = {
template
}接着我们在test文件中写我们的操作dom的代码
const { template } = require('./client')
test('simulate-dom-click', () => {
document.body.innerHTML = `${template}${script}`;
const target = document.getElementsByClassName('test')[0];
const btn = document.getElementById('btn');
btn.addEventListener('click', () => {
console.log(111)
const target = document.getElementsByClassName('test')[0]
target.innerHTML = '666'
})
btn.click();
expect(target.innerHTML).toEqual('666')
}) 接着运行我们的文件

ok,操作成功!
那么我们的client学习就暂时到这了。。。
一些常用的Matcher方法#
toBe:基于Object.is[1] ,相当于===toEqual: 会递归整个对象来比较所有字段对应的值是否相同。toMatch:相当于String.match,传入一个正则。toContain:用于可遍历的,比如数组,相当于Array.includestoThrow:用于匹配错误的,可以传正则用来匹配,也可以直接传字符串。
其它#
- 官方还提供了一些钩子,比如
beforeEach、afterEach等,这里就不多说了,感兴趣的大佬可自行去翻阅。
https://jestjs.io/docs/setup-teardownjestjs.io/docs/setup-teardown
- 还有提供了
mock的功能,这里也不多说。
https://jestjs.io/docs/mock-functionsjestjs.io/docs/mock-functions
- 另外当你安装
jest的时候,你会看到拉了很多个jest-xx的包,这些包是官方自己的工具包,同时也可以被当作独立包引入。比如jest-diff[2]。
const {diff} = require('jest-diff');
const a = {a: {b: {c: 5}}};
const b = {a: {b: {c: 6}}};
const result = diff(a, b);
// print diff
console.log(result);
全局安装jest脚手架#
Jest CLI Options · Jestjestjs.io/docs/cli
你也可以全局安装这个jest脚手架
npm install jest -g
jest --init执行完jest --init方法之后会有一个对话框, 根据对话选项会帮你初始化一些参数,比如是否使用typescript又或者是测试浏览器端代码js-dom
回答完之后会在你当前目录下生成一个jest.config.js文件。
接着你可以在终端执行就会帮你将文件夹下的所有测试集都跑一遍,如果你有配置coverage,就会输出一个文档。
jest ${你的项目baseName} --config=jest.config.js

但如果你不想执行项目里所有的测试集,你可以改变参数。
jest ${项目baseName}/${次级文件夹}/... --config=jest.config.js // 只运行其中一个文件夹
jest ${项目baseName}/${次级文件夹}/.../xxx.test.js --config=jest.config.js // 只运行其中一个文件夹的一个文件
jest -t ${test/describe函数中传入的描述字段,作为key在这里可以用于匹配} --config=jest.config.js // 只运行其中一个文件夹的一个文件中的一个测试元或者你不想每次都自己手动去跑指令,你可以全局watch他们。
jest -t ${test/describe函数中传入的描述字段,作为key在这里可以用于匹配} --watch // 只监听一个测试集
jest ${项目baseName}/${次级目录名}/xxx.test.js --watch // 只监听一个文件中的测试集
jest --watchAll // 监听所有这里就介绍这么多,感兴趣的大佬可以自行翻阅官方文档。
推荐工具#
推荐两个vscode的拓展,第一个会自动运行你的测试集,如果不想的话可以用下面那个,手动跑,但不需要在终端执行指令。

我更喜欢第二个,因为第一次每次改动都会跑一次。。。而且是全局的,也就是说别的项目中有的话也会跑起来。。
第二个在test.js文件中右键即可跑整个文件。

如果你只是想运行这个文件中的一个测试元,那么可以点击这个test函数左上角的run/debug

非常方便。
实战#
说了这么多,但是都只是些简单的demo。还是得实战一下才行。
我们就实战vue项目,其它的大佬感兴趣可以自行学习翻阅官方文档。
官网有提供相关的文档
但官方的文档依旧停留在2017 ,这时用的是vue2,未免有些“老”,我们直接从vue3开始。
我们就先不用vite创建了,因为vite有一个更适合的vitest[3] 。如果大佬感兴趣可以去看下。我们还是老规矩用webpack + vue-loader来配置。
npm init --yes
npm install webpack webpack-dev-server webpack-cli vue-loader html-webpack-plugin babel-jest @babel/core @babel/preset-env -D
npm install @vue/vue3-test -D
npm install vue --save-dev
echo > webpack.config.js
echo > index.html
echo > main.js
mkdir views
cd views
echo > index.vue其中需要注意babel-jest、jest的版本需要控制在27.x,不然你等着无限报错吧。。。

Getting Started | Vue Test Utilstest-utils.vuejs.org/guide/#what-is-vue-test-utils
然后配置webpack.config.js
const { VueLoaderPlugin } = require('vue-loader')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: './main.js',
output: {
path: require('path').resolve(__dirname, 'dist'),
filename: '[name].[fullhash].js'
},
mode: 'development',
module: {
rules: [
{
test: /\.vue$/,
use: ['vue-loader']
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './index.html'
}),
new VueLoaderPlugin()
]
}然后配置main.js
import { createApp } from 'vue'
import App from './views/index.vue'
const app = createApp({
name: 'App',
...App
})
app.mount('#app');
然后index.html中配置下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>vue-jest</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
index.vue随便写点东西,写一个每次点击+1的方法
<template>
<div :testid="'test'" class="test" @click="changeNum">{{ num }}</div>
</template>
<script setup>
import { ref } from "vue";
const num = ref(0);
function changeNum() {
num.value += 1;
}
</script>
<style lang="scss" scoped></style>最后package.json 中新增script
"serve": "webpack-dev-server"
"test": "jest"然后运行serve起来没问题就先不管了。
接着创建babel.config.js文件用来支持esm环境
module.exports = {
presets: [
['@babel/preset-env', { targets: { node: 'current' } }]
]
}
创建完之后再回到jest.config.js中,我们需要将环境支持改为jest-environment-jsdom以及需要支持vue文件还有文件的转换。
module.exports = {
// Automatically clear mock calls, instances, contexts and results before every test
clearMocks: true,
// An array of file extensions your modules use
moduleFileExtensions: [
"js",
"mjs",
"cjs",
"jsx",
"ts",
"tsx",
"json",
"node",
'vue'
],
// The test environment that will be used for testing
testEnvironment: "jest-environment-jsdom",
// Options that will be passed to the testEnvironment
testEnvironmentOptions: {},
// A map from regular expressions to paths to transformers
transform: {
"^.+\\.js$": "babel-jest",
"^.+\\.vue$": "@vue/vue3-jest"
},
}; 其中transform中是需要转换的文件类型,缺一不可。
需要注意的一点是如果你设置了environment-jsdom,那么你得先确保你没有安装jest-environment-jsdom这个包,不然也是一顿报错。
我们创建一个__tests__文件夹,并且在这个文件夹里创建一个index.spec.js的文件。
import { mount } from '@vue/test-utils'
import Index from '../views/index.vue'
test('test-click', async () => {
const wrapper = mount(Index)
console.log(wrapper.text())
expect(wrapper.text()).toContain('0')
await wrapper.trigger('click')
expect(wrapper.text()).toContain('1')
})ok`,我们终于配置完了。直接在终端执行我们的命令`npm run test
谢天谢地,我怎么会沙雕的跑去用webpack而不是直接用vue-cli。。。。结果各种问题,排查了两个小时。。。
如果大佬你觉得有用麻烦点个赞让我回回血。。。谢谢!!!
参考#
- ^Object.is https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/is
- ^jest-diff https://jestjs.io/docs/jest-platform#jest-diff
- ^vitest https://vitest.dev/guide/
发布于 2022-10-21 19:53
