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

Vue3中使用vue-testing-library fireEvent时PrimeVue InputNumber无法更新ref

PrimeVue InputNumber单元测试中ref无法更新的问题

问题背景

使用Vue 3.4、vue-testing-library 8.0.2和vitest时,单元测试里PrimeVue InputNumber组件的v-model绑定ref无法更新,但浏览器中运行完全正常。

组件代码(Component.vue)

<script setup lang="ts">
import { ref } from 'vue';

const inputtext = ref('a');
const inputnumber = ref(0);
const spinbutton= ref(1);
</script>
<template>
  <div>
    <InputText id="test" v-model="inputtext" aria-label="inputtext" />
    <InputNumber v-model="inputnumber" input-id="count" aria-label="inputnumber" />
    <input v-model="spinbutton" role="spinbutton" aria-label="spinbutton" />
    <div data-testid="test">
      {{ inputtext }} {{ inputnumber }} {{ spinbutton }}
    </div>
  </div>
</template>

单元测试代码(Component.spec.ts)

import { fireEvent, screen, render } from '@testing-library/vue';
import PrimeVue from 'primevue/config';

import MyCounter from './Component.vue';

describe('MyCounter', () => {
  it('should works', async () => {
    render(MyCounter, { props: {}, global: { plugins: [PrimeVue] } });

    expect(screen.getByTestId('test')).toHaveTextContent('a 0 1');

    let component = screen.getByLabelText('inputtext');
    await fireEvent.update(component, 'b');
    expect(component).toHaveValue('b');

    component = screen.getByLabelText('inputnumber');
    await fireEvent.update(component, '2');
    expect(component).toHaveValue('2');

    component = screen.getByLabelText('spinbutton');
    await fireEvent.update(component, '3');
    expect(component).toHaveValue('3');

    expect(screen.getByTestId('test')).toHaveTextContent('b 2 3');
  });
});

测试结果

Expected : b 2 3 
Received : b 0 3

疑问

为何inputnumber对应的ref没有更新?


原因与解决方案

原因

PrimeVue InputNumber是封装后的组件,并非原生input元素:

  1. fireEvent.update直接作用于组件根元素时,无法触发内部绑定的更新逻辑;
  2. 测试中传入字符串'2',组件在测试环境下未自动完成数值类型转换,导致v-model同步失败。

解决方案

方案1:操作组件内部的原生input

通过input-id定位到InputNumber内部的原生input元素,再触发事件:

component = screen.getByLabelText('inputnumber');
// 获取内部原生input
const nativeInput = component.querySelector('#count') as HTMLInputElement;
await fireEvent.update(nativeInput, '2');
// 额外触发input事件确保组件更新
await fireEvent.input(nativeInput);
expect(screen.getByTestId('test')).toHaveTextContent('b 2 3');

方案2:使用userEvent模拟真实交互

@testing-library/user-event能更准确模拟用户操作,替代fireEvent:

  1. 安装依赖:
npm install @testing-library/user-event --save-dev
  1. 修改测试代码:
import { screen, render } from '@testing-library/vue';
import userEvent from '@testing-library/user-event';
import PrimeVue from 'primevue/config';

import MyCounter from './Component.vue';

describe('MyCounter', () => {
  it('should works', async () => {
    const user = userEvent.setup();
    render(MyCounter, { props: {}, global: { plugins: [PrimeVue] } });

    expect(screen.getByTestId('test')).toHaveTextContent('a 0 1');

    // 操作InputText
    const textInput = screen.getByLabelText('inputtext');
    await user.clear(textInput);
    await user.type(textInput, 'b');

    // 操作InputNumber内部原生input
    const numberInput = screen.getByLabelText('inputnumber').querySelector('#count') as HTMLInputElement;
    await user.clear(numberInput);
    await user.type(numberInput, '2');

    // 操作原生spinbutton
    const spinInput = screen.getByLabelText('spinbutton');
    await user.clear(spinInput);
    await user.type(spinInput, '3');

    expect(screen.getByTestId('test')).toHaveTextContent('b 2 3');
  });
});

方案3:直接触发组件的change事件

传入数值类型的参数触发组件的change事件:

component = screen.getByLabelText('inputnumber');
await fireEvent.change(component, { target: { value: 2 } });
expect(screen.getByTestId('test')).toHaveTextContent('b 2 3');

内容的提问来源于stack exchange,提问作者wintenbb

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 18:51:15