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元素:
fireEvent.update直接作用于组件根元素时,无法触发内部绑定的更新逻辑;- 测试中传入字符串
'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:
- 安装依赖:
npm install @testing-library/user-event --save-dev
- 修改测试代码:
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
相关产品推荐
相关产品推荐

