You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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 );
  } );
} );

运行测试时先后遇到两个错误:

  1. 第一个错误:
Cannot read properties of undefined (reading 'focus')
TypeError: Cannot read properties of undefined (reading 'focus')
  1. 设置模拟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. 错误根源分析

  • 第一个错误:未提供filter getter,组件计算属性filterName尝试读取this.$store.getters.filter.name时,filter为undefined,触发报错;同时shallowMount会将Vuetify的v-text-field浅渲染为占位组件,导致this.$refs.filter无法获取到带focus方法的实例。
  • 第二个错误:模拟的filter getter返回的是函数,而非组件期望的包含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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 01:15:44