🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
![](https://img.kancloud.cn/9e/fe/9efe790a9083cf791b77ef6101f3ba79_550x362.png) ![](https://img.kancloud.cn/51/db/51db8c6d59dde3e8ad14039a24c19c4b_614x247.png) https://juejin.cn/post/7039108357554176037#heading-13 https://juejin.cn/post/6844904196244766728#heading-15 ## jest 单元测试(TDD) 1. 安装jest: npm install --save-dev jest 2. 运行`npx jest --init`命令,生成一份jest的配置文件`jest.config.js` 3. 运行`npm i babel-jest @babel/core @babel/preset-env -D`安装`babel`,并且配置`.babelrc` ``` {"presets": ["@babel/preset-env", "@babel/preset-typescript"], "plugins": ["@babel/plugin-transform-runtime"]} ``` 4. 在根目录下建立test/unit文件夹,创建文件夹utils 和components,创建文件 xxx.spec.js或 xxx.test.js 5. package.json增加命令 ```js "test:unit": "jest --clearCache && vue-cli-service test:unit" "test-watch": "jest --watch" // 让jest监听文件变化 "coverage": "jest --coverage" // 生成测试覆盖率文件 ``` - 用法: ```js description("src/pages/components/auth", () => { test("should return 100", () => { // const result = ... expect(result).toEqual(100); }); it("should not return 100", () => { // const result = ... expect(result).not.toBe(100); }); }); ``` - **常用断言方法: jest-matchers** >.not 修饰符 --- .not.toBe(5) .toEqual() --- 匹配器会递归的检查对象所有属性和属性值是否相等,常用来检测引用类型 .toThorw() ---- 被测试方法是否按照预期抛出异常 .toMatch() ---- 传入一个正则表达式,它允许我们来进行字符串类型的正则匹配 toBeGreaterThan() ---- 判断数num是否大于某个数 expect(set).toContain('Curry') ---- // toContain 判断数组或者集合是否包含某个元素 expect(arr).toHaveLength(3) ---- // toHaveLength 判断数组的长度 `jest.fn()`模拟函数 - **测试--异步--定时器** 1. 使用`jest.fn()`生成一个`jest`提供的用来测试的函数,之后回调函数不需要再写一个; 2. 使用`jest.useFakeTimers()`方法启动`fakeTimers`,模拟真实的定时器--跳过定时器等待时间直接执行内部逻辑; 3. 通过`jest.advanceTimersByTime()`方法,参数传入毫秒时间,`jest`会立即跳过这个时间值,还可以通过;`toHaveBeenCalledTimes()`这个`mathcer`来测试函数的调用次数。 ```js export default (fn) => { setTimeout(() => { fn() }, 2000) } // 测试 -- 定时器 import timeout from './timeout' test('测试timer', () => { jest.useFakeTimers() const fn = jest.fn() timeout(fn) jest.advanceTimersByTime(2000) // 时间快进2秒 expect(fn).toHaveBeenCalledTimes(1) }) ``` 如果多个test函数都使用`fakeTimers`,一定要在`beforeEach`勾子中每次都调用`jest.useFakeTimers()`,否则多个test函数中的fakeTimers会是同一个,将会互相干扰。 `beforeEach(() => { jest.useFakeTimers() })` - **测试--异步--数据请求`(promise/async await)`** ```js export const request = () => { return axios.get('https://jsonplaceholder.typicode.com/todos/1') } // 写法一 test('测试request', async () => { const res = await request() expect(res.data).toEqual({ "userId": 1, "id": 1 }) }) // 写法二 test('测试request', () => { expect(request()).resolves.toMatchObject({ data: { "userId": 1, "id": 1 } }) }) // 第三种 test('测试request', () => { request().then(data => { expect(data.data).toEqual({ "userId": 1, "id": 1, }) }) }) // 对于请求出现错误的测试 ----- 请求一个不存在的接口地址,会返回404 test('测试request 404', () => { return requestErr().catch((e) => { expect(e.toString().indexOf('404') > -1).toBeTruthy() }) }) ``` - Hamburger.spec.js 测试文件 ---测组件 ```js import { shallowMount } from '@vue/test-utils' import Hamburger from '@/components/Hamburger/index.vue' describe('Hamburger.vue', () => { it('toggle click', () => { const wrapper = shallowMount(Hamburger) const mockFn = jest.fn() wrapper.vm.$on('toggleClick', mockFn) wrapper.find('.hamburger').trigger('click') expect(mockFn).toBeCalled() }) it('prop isActive', () => { const wrapper = shallowMount(Hamburger) wrapper.setProps({ isActive: true }) expect(wrapper.contains('.is-active')).toBe(true) }) }) ``` - param2Obj.spec.js 测公用方法 ```js import { param2Obj } from '@/utils/index.js' describe('Utils:param2Obj', () => { const url = 'https://github.com/PanJiaChen/vue-element-admin?name=bill&age=29&sex=1&field=dGVzdA==&key=%E6%B5%8B%E8%AF%95' it('param2Obj test', () => { expect(param2Obj(url)).toEqual({ name: 'bill', age: '29', sex: '1', field: window.btoa('test'), key: '测试' }) }) }) // window.btoa创建base-64编码的字符串 ``` ## **jest.config.js 配置文件** ```js module.exports = { // 文件后缀 moduleFileExtensions: ['js', 'jsx', 'json', 'vue'], // 文件如何转换 transform: { '^.+\\.vue$': 'vue-jest', '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub', '^.+\\.jsx?$': 'babel-jest' }, // 忽略的文件 transformIgnorePatterns: ['/node_modules/'], // 生成快照需要的插件 snapshotSerializers: ['jest-serializer-vue'], // 需要执行哪些目录下的测试用例 testMatch: [ '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)' ], // 在执行用例前的配置文件 setupFiles: ['./tests/setup.js'], // 测试覆盖率配置 collectCoverage: true, coverageReporters: ['html', 'lcov', 'text-summary'], coverageDirectory: './test/coverage', collectCoverageFrom: ['components/**/*.vue'], testURL: 'http://localhost/' } ```