Vue3使用vue-test-utils与Jest测试setup计算值setData报错
问题根因
你遇到TypeError: Cannot add property myData, object is not extensible报错有两个核心原因:
- 组件
setup()里没有把myData暴露到组件实例上:setup的return只返回了isEditable,Vue组件实例在setup执行完成后会被冻结(不可扩展),后续调用setData往实例上新增不存在的myData属性,就会触发不可扩展的错误。 - 测试实现思路存在偏差:
setup内部声明的局部响应式变量如果没有return,属于组件内部私有状态,外部测试代码无法直接通过setData覆盖,setData仅能修改组件已经对外暴露的、已存在的实例属性。
可行解决方案
方案1:改造组件,把需要测试控制的状态暴露为props(推荐,更符合组件设计规范)
把myObject作为组件入参,测试时直接传参即可,不需要用setData硬改内部状态:
改造后的Hello.vue:
<template> <div v-if="isEditable" id="myEditDiv"> <button type="button"> Edit </button> </div> </template> <script lang="ts"> import { computed, defineComponent } from "vue" export default defineComponent({ props: { myObject: { type: Object, default: () => ({myName:"", myNumber:""}) } }, setup(props) { const isEditable = computed(() => { return !props.myObject.myName.startsWith('DNU') }) return { isEditable } } }) </script>
对应的测试用例Hello.spec.ts:
import { shallowMount } from '@vue/test-utils' import Hello from '@/components/Hello.vue' describe('Hello.vue Test', () => { it('isEditable returns FALSE if NAME starts with DNU', async () => { const testMyObject = {myName:"DNU Bad Name", myNumber:"12345"} // 挂载时直接传props,不需要setData const wrapper = shallowMount(Hello, { props: { myObject: testMyObject } }) expect(wrapper.vm.isEditable).toBe(false) // 校验DOM渲染结果可正常执行 expect(wrapper.find('#myEditDiv').exists()).toBe(false) }) })
方案2:保留内部状态模式(不推荐)
如果你确实要把myData作为组件内部状态,需要先在setup里把它return出来,让它成为组件实例上已存在的属性,后续setData才能正常修改:Hello.vue的setup返回值修改为:
return { isEditable, myData // 必须先暴露,否则实例上不存在这个属性,setData新增就会报错 }
这种方式测试可信度更低,因为直接修改组件内部私有状态的逻辑,和组件实际运行场景差异较大。
注意:不要尝试在测试中修改setup内部未暴露的私有变量,Vue3的setup闭包内的变量对外是不可访问的,
setData没有权限穿透闭包修改内部局部变量。
内容的提问来源于stack exchange,提问作者fogo
相关产品推荐
相关产品推荐

