Vue组件文本框焦点测试报错排查及挂载焦点验证咨询
Vue FilterBox组件测试问题排查与解决方案
问题背景
我是Vue新手,需要为项目中的FilterBox组件编写测试用例,组件代码如下:
<template> <v-form> <v-container> <v-text-field v-on:keydown.enter.prevent v-model="filterName" dense clearable style="transition: all 0.3s ease; width: 12rem;" class="mt-6" @focus="isFocused = true" @blur="isFocused = false" ref='filter' > </v-text-field> </v-container> </v-form> </template> <script> export default { name: "FilterBox", data: () => ({ isFocused: false, }), computed: { filterName: { get() { return this.$store.getters.filter.name }, set(value) { this.$store.commit('set_filter_name', value) } } }, mounted() { this.$refs.filter.focus() }, } </script>
编写的测试代码如下:
import Vue from "vue"; import Vuex from "vuex"; import Vuetify from "vuetify"; import { shallowMount } from "@vue/test-utils"; import FilterBox from "@/components/FilterBox"; Vue.use( Vuex ); Vue.use( Vuetify ); describe( 'Filter Box', () => { let store; let getters; let vuetify; beforeEach( () => { vuetify = new Vuetify(); } ); it( 'should return a valid wrapper of the component', function () { getters = {}; store = new Vuex.Store( { getters } ); const wrapper = shallowMount( FilterBox, { store, vuetify } ); expect( wrapper.exists() ).toBe( true ); } ); } );
运行测试时先后遇到两个错误:
- 第一个错误:
Cannot read properties of undefined (reading 'focus') TypeError: Cannot read properties of undefined (reading 'focus')
- 设置模拟getters后:
getters = { filter: () => { return jest.fn() } }; store = new Vuex.Store( { getters } );
出现第二个错误:
this.$refs.filter.focus is not a function TypeError: this.$refs.filter.focus is not a function
需要排查问题并指导如何测试文本框在mounted钩子中是否成功获得焦点。
问题原因与解决方案
1. 错误根源分析
- 第一个错误:未提供
filtergetter,组件计算属性filterName尝试读取this.$store.getters.filter.name时,filter为undefined,触发报错;同时shallowMount会将Vuetify的v-text-field浅渲染为占位组件,导致this.$refs.filter无法获取到带focus方法的实例。 - 第二个错误:模拟的
filtergetter返回的是函数,而非组件期望的包含name属性的对象,读取filter.name会出错;且v-text-field的浅渲染问题仍未解决,focus方法不存在。
修正后的测试代码
import Vue from "vue"; import Vuex from "vuex"; import Vuetify from "vuetify"; import { shallowMount, createLocalVue } from "@vue/test-utils"; import FilterBox from "@/components/FilterBox"; // 创建本地Vue实例,避免污染全局Vue环境 const localVue = createLocalVue(); localVue.use(Vuex); localVue.use(Vuetify); describe('Filter Box', () => { let store; let vuetify; let mockFocus; beforeEach(() => { vuetify = new Vuetify(); // 模拟focus方法,用于验证调用 mockFocus = jest.fn(); // 正确模拟store的getters,返回符合组件期望的结构 const getters = { filter: () => ({ name: '' }) }; store = new Vuex.Store({ getters }); }); it('should render the component correctly', () => { const wrapper = shallowMount(FilterBox, { localVue, store, vuetify, // 存根v-text-field,提供focus方法和v-model所需的基础结构 stubs: { VTextField: { template: '<input ref="input">', methods: { focus: mockFocus }, props: ['value'], emits: ['input'] } } }); expect(wrapper.exists()).toBe(true); }); it('should call focus method on text field when mounted', () => { shallowMount(FilterBox, { localVue, store, vuetify, stubs: { VTextField: { template: '<input>', methods: { focus: mockFocus }, props: ['value'], emits: ['input'] } } }); // 验证mounted钩子是否调用了focus方法 expect(mockFocus).toHaveBeenCalled(); }); });
扩展:测试真实DOM焦点
如果需要验证真实的DOM焦点行为,可改用mount而非shallowMount(需确保测试环境支持DOM API,如jsdom),通过检查document.activeElement来验证:
it('should focus the text field when mounted', async () => { const wrapper = mount(FilterBox, { localVue, store, vuetify }); // 等待组件挂载完成 await Vue.nextTick(); // 获取真实的input元素 const input = wrapper.find('input').element; expect(document.activeElement).toBe(input); });
内容的提问来源于stack exchange,提问作者Dairelys García Rivas
相关产品推荐
相关产品推荐

