0


接入jest单元测试常见问题收集

前言

前一段时间公司让前端写单元测试,于是乎就研究了下JEST。从安装到落地一路坎坷呀,因为淋雨过所以想为人撑伞,把自己遇到的问题和搜集到的都分享出来,欢迎大家补充内容。(技术是没边界的,即使我在公司内部也分享过,仍不影响继续分享给大家)

一、安装包并运行命令后报错类问题

1、未安装测试运行器运行环境jest-environment-jsdom

window is undefined, vue-test-utils needs to be run in a browser environment.You can run the tests in node using jsdom
早期的jest会默认安装JSDOM的运行环境,但是在28版本后,已取消默认安装,需要自己手动安装jest-environment-jsdom,执行jest运行环境。

npminstall --save-dev jest-environment-jsdom

在package.json中加入

// package.json{// ..."jest":{// ..."testEnvironment":"jsdom"}}

2、安装jest-babel报错

2.1 提示:Error: Cannot find module ‘babel-core’

查找issue后发现,主要是因为babel-loader和babel-core版本不匹配导致的。
babel-loader v8.x 应该对应 babel-core v7.x
babel-loader v7.x 应该对应 babel-core v6.x
所以需降低babel-core版本:

npminstall --save-dev babel-core@^7.0.0-bridge.0

2.2 提示:Requires Babel “^7.0.0-0”, but was loaded with “6.26.3”.

描述:
安装

jest-babel

后,提示让安装

babel-core

,执行

npm install --save-dev babel-core

安装后版本不匹配报错。
解决:
安装指定版本 “babel-core”: “^7.0.0-bridge.0”

npm uninstall jest babel-jest babel-core @babel/core
npminstall --save-dev jest babel-jest babel-core@^7.0.0-bridge.0 @babel/core

3、提示:ReferenceError: Vue is not defined

查找 issue 后发现,其原因主要是 jest-environment-jsdom 和@vue/test-utils配置冲突导致的。引入 jest-environment-jsdom时,jest会自动设置特定的导入方式,再引入@vue/test-utils时设置了customExportConditions配置项,导致Jest 最后使用了CommonJS的导入方式,修改如下:

// package.json
{
  // ...
  "jest": {
   // ...
   "testEnvironmentOptions": {
     "customExportConditions": ["node", "node-addons"]
   },
  }
}

4、提示:TypeError: Cannot destructure property ‘createComponentInstance’ of ‘Vue.ssrUtils’ as it is undefined

此问题是由于@vue/test-utils与vue版本不匹配导致的,按照官网的描述:
@vue/test-utils v2.x 应该对应 vue v3.x
@vue/test-utils v1.x 应该对应 vue v2.x
需对@vue/test-utils进行降级处理:

npminstall--save @vue/[email protected]

二、测试用例编写过程遇到的坑点

1、在测试组件中使用了第三方组件或其他基础组件,如Element UI等

报错场景包括:

  • 测试组件使用了全局定义的第三方组件 Vue.use(ElementUI, { locale, size: 'small' })
  • 测试组件按需引入第三方组件 import { Input } from 'element-ui'
  • 自定义基础组件 import CustomerInput from '@/components/CustomerInput.vue'

解决方式如下:

  1. 在挂载选项中注册对应的组件
const wrapper =mount(CustomerInputNumber,{components:{'el-input': Input,'CustomerInput': CustomerInput
  }});

其中,注册组件可以是自定义的模拟组件,也可以在测试用例中按需引入的第三方组件

import CustomerInput from'@/components/CustomerInput.vue'const mockComponent ={template:"<div><slot></slot></div>",props:{color: String
  }};const wrapper =mount(CustomerInputNumber,{components:{'el-input': mockComponent,'CustomerInput': CustomerInput
  }});
  1. 使用临时Vue实例全局注册组件
import{ createLocalVue }from'@vue/test-utils'import{ Input }from'element-ui'const localVue =createLocalVue()
localVue.use(Input)const wrapper =mount(CustomerInputNumber,{
  localVue
});

2、在测试组件中直接使用node_modules中的文件

例如,在测试组件中,直接使用了node_modules中的Emitter组件。但是在Jest转译时,会默认将node_modules忽略,所以当测试用例执行到该行时,会因为获取不到导出的Emitter组件而报错。
在报错信息中,Jest给出的处理建议,是将Emitter组件所在目录加入到转译文件中,需要对Jest做如下配置:

// package.json{// ..."jest":{// ..."transformIgnorePatterns":["<rootDir>/node_modules/(?!(element-ui/src/mixins/emitter))"],}}

3、在测试组件的Constructer中使用全局依赖

如果提示在测试组件中找不到挂载在全局实例上的方法,如 lodash.throttle() 而在实际的测试用例运行时,已经传入了loadsh的mocks注册,并且在测试组件的mounted生命周期中,Vue实例上可获取到mocks注册的loadsh属性。

// 测试用例import lodash from'lodash'const wrapper =mount(TestComponent,{mocks:{"_": lodash
  },});
// 测试组件exportdefaultclassTestComponentextendsVue{// private onScrollListener = this._.throttle(this.onScroll, 300) // 先注释报错行privatemounted(){
    console.log(this._)// 可以正常输出}}

原因是,运行测试用例挂载(mount、shallowMount)测试组件时,如果测试组件使用了全局注册的属性或方法,只能在mounted生命周期后才能获取到由测试用例mocks注册的模拟属性或方法。而Constructor生命周期在mounted之前执行,此时去获取就会提示undefined。
解决方法是:在测试组件中按需引入lodash属性,不使用全局注册,这样就能在Constructor生命周期中获取到依赖:

// 测试组件import _ from'lodash'exportdefaultclassTestComponentextendsVue{private onScrollListener = _.throttle(this.onScroll,300)}

4、提示:SyntaxError: Unexpected token ‘:’ jest单测样式问题

描述:jest单测样式问题

image.png

处理方式:
1) 安装并配置 identity-obj-proxy,可以进行样式的代理处理
  • (1)单独jest配置文件:
module.export ={'moduleNameMapper':{'\\.(css|less)$':'identity-obj-proxy'}}
  • (2)package.json进行配置:
"jest":{"moduleNameMapper":{"\\.(css|less)$":"identity-obj-proxy"}}
2) 创建mock文件

注:rootDir相当于package.json文件的路径

  • 单独jest配置文件:
module.export ={'moduleNameMapper':{'\\.(css|less)$':'<rootDir>/__mocks__/styleMock.js'}}
  • 文件目录./mocks/styleMock.js内:image.png
module.exports = {};

5、提示:Cannot read property ‘_t‘ of undefined Unknown custom 单元测试 jest element-ui国际化i18n报错解决

报错点:

  • 1.Cannot read property ‘_t’ of undefined
  • 2.Unknown custom element: - did you register the component correctly?
// 1导入:vue测试工具类// import Vue from 'vue';import{shallowMount, mount, createLocalVue }from'@vue/test-utils';const localVue =createLocalVue();// 2导入:element-uiimport ElementUI from'element-ui';import'element-ui/lib/theme-chalk/index.css';// 3导入:vueximport store from'**/store/index.js';// 4导入:mixinimport mixin from'**/views/js/mixin.js';
localVue.mixin(mixin);// 5导入:要测试的组件import myComponents from'**/views/myComponents.vue';/* --------中引文switch start----------------- */import VueI18n from'vue-i18n';import enLocale from'element-ui/lib/locale/lang/en';import zhLocale from'element-ui/lib/locale/lang/zh-CN';import zhmsg from'../../public/lang/zh.json';import enmsg from'../../public/lang/en.json';

localVue.use(VueI18n)const messages ={en:{...enmsg,...enLocale
  },zh:{...zhmsg,...zhLocale
  }};// Create VueI18n instance with options 中引文switchvar i18n =newVueI18n({locale:'zh',
  messages,// set locale messagessilentTranslationWarn:true});
window.Languages = i18n;// 这里全部注册element:解决报错2
localVue.use(ElementUI,{i18n:function(path, options){let value = i18n.t(path, options);if(value !==null&& value !==undefined)return value;return'';}});// 6.更多匹配器请看官网 (jest官网 https://www.jestjs.cn/)var wrapper =mount(myComponents,{
  localVue,
  store,mocks:{$t:(a,b)=> i18n.t(a,b),// 这里处理国际化:解决报错1},});// 测试套件describe('myComponents-vue组件',()=>{// 测试用例// 1.测试data上面的值test('测试data上面的值:',()=>{expect(wrapper.vm.msg).toBe('myComponents value');});test('点击按钮:',()=>{
    console.log('wrapper.vm.userInfo:',wrapper.vm.userInfo);let btnT = wrapper.find('#btn').text()
    console.log('btnT-text:', btnT);expect(btnT).toBe('确定');});// 2.调用组件的方法methodtest('调用组件的方法method:--登录方法',async()=>{// 准备数据
    wrapper.vm.form ={username:'admin',password:'123456'}
    wrapper.vm.sureLogin();
    console.log('this.userInfo.username', wrapper.vm.userInfo.username);expect(wrapper.vm.userInfo).toBeTruthy()});test('关闭按钮:',()=>{let btnT = wrapper.find('#cancelBtn').text()
    console.log('btnT:', btnT);expect(btnT).toBe('关闭');});});

6、提示:TypeError: Cannot read property ‘query’ of undefined vue jest路由器路由错误 this.$route.query

import{ createLocalVue, shallowMount }from'@vue/test-utils'import VueRouter from'vue-router'import myComponents from'**/views/myComponents.vue';const localVue =createLocalVue();
localVue.use(VueRouter)const router =newVueRouter();describe('myComponents.vue',()=>{let wrapper;beforeEach(()=>{
    wrapper =shallowMount(myComponents,{
      localVue,
      router
    });});it('renders correctly',()=>{expect(wrapper.element).toMatchSnapshot();});})

7、提示:TypeError: symbol is not a function 被测vue组件中使用require 引入图片报错

image.png
在Vue.js项目中使用 require 引入图片时,如果你想在单元测试中使用Jest跳过这些引入,可以使用Jest的jest.mock功能来模拟这些require调用。

以下是一个简单的例子,展示了如何模拟图片引入:

假设你有一个组件MyComponent.vue,其中使用require来引入一个图片:

<template><div><img:src="imageSrc"alt="example image"></div></template><script>exportdefault{data(){return{imageSrc:require('@/assets/example.png')};}};</script>

在你的单元测试文件中,你可以使用jest.mock来模拟require:

import{ shallowMount, createLocalVue }from'@vue/test-utils';import MyComponent from'@/components/MyComponent.vue';
 
jest.mock('@/assets/example.png',()=>'mocked-image-url');describe('MyComponent.vue',()=>{let wrapper;beforeEach(()=>{const localVue =createLocalVue();
    wrapper =shallowMount(MyComponent,{ localVue });});it('renders correctly',()=>{expect(wrapper.element).toMatchSnapshot();});});

在这个例子中,jest.mock函数模拟了@/assets/example.png的返回值为’mocked-image-url’,这样在测试执行时,require调用就会被这个模拟值所替代,从而不会试图加载实际的图片文件,避免了测试执行时的错误。

标签: 单元测试

本文转载自: https://blog.csdn.net/m0_37437840/article/details/142383231
版权归原作者 佚名猫 所有, 如有侵权,请联系我们删除。

“接入jest单元测试常见问题收集”的评论:

还没有评论