Vue.js单元测试中自定义输入组件v-model验证逻辑失效求解
问题根本原因
你遇到的问题本质是原生输入框的实际值和Vue绑定的props.value出现了不同步:
当你通过setValue或者手动输入修改原生input的内容时,DOM的value会先更新,再触发input事件。如果过滤后的结果和当前props.value完全相等,Vue检测到数据没有变化,就不会触发重渲染来覆盖原生input的非法值,导致最终断言失败。
最优解决方案
完全不需要额外声明ref引用,直接通过input事件的事件对象操作目标元素即可,代码改动极小,符合Vue自定义输入组件的实现规范:
<template> <div class="custom-input"> <label :class="labelClasses" :for="name">{{ label }}</label> <input :class="`custom-input-${name}`" :name="name" :maxlength="3" type="text" :value="value" @input="onInput" @keypress="onlyLettersFilter" /> </div> </template> <script> export default { props: { name: { type: String, required: true }, placeholder: { type: String, default: "some placeholder" }, label: String, value: String, }, computed: { hasValue() { return this.value?.length > 0; }, labelClasses() { return "custom-input--label " + (this.hasValue ? "active" : ""); }, }, methods: { filterValue(input) { let value = input.slice(0, 3); value = value.toUpperCase().replace(/([^A-Z])+/g, ""); return value; }, onInput(event) { const filteredValue = this.filterValue(event.target.value); // 直接同步原生输入框的值,保证显示永远符合过滤规则 event.target.value = filteredValue; // 仅在值实际变化时emit事件,避免父组件不必要的更新 if (filteredValue !== this.value) { this.$emit("input", filteredValue); } }, onlyLettersFilter(event) { if (event.keyCode === 13) return true; var key = String.fromCharCode( !event.charCode ? event.which : event.charCode ); var regex = new RegExp(/[a-zA-Z]+/); if (regex.test(key)) { return true; } event.preventDefault(); return false; }, }, }; </script>
方案说明
- 不需要额外添加ref、不需要操作DOM引用,直接复用input事件自带的target对象即可实现原生值同步
- 同步逻辑只在输入触发时执行,不会增加额外的响应式监听开销
- 保留了仅在值变化时才emit事件的逻辑,不会影响父组件的性能
- 完全覆盖测试场景:即使过滤后的值和原有value相同,原生input的内容也会被重置为过滤后的值,测试断言可以正常通过
内容的提问来源于stack exchange,提问作者Jose Truyol
相关产品推荐
相关产品推荐

