Shopware 6 Vue组件Jest测试出现无限更新循环问题求助
Vue组件测试出现无限更新循环问题排查与修复
环境版本
- Vue 2.6.14
- Vuex 3.1.1
- Vue Test Utils 1.3.0
- Jest 29.3
问题背景
组件在开发环境运行正常,但使用Jest结合Vue Test Utils进行组件测试时,抛出「组件渲染函数存在无限更新循环」错误。注释掉Shopware.State.commit('MyPluginStore/setLoading', ['productComponents', true])后,测试即可通过。
组件代码(my-component/index.js)
const { Component } = Shopware; Component.register('my-component', { template: '<sw-number-field :value="computedComponentPurchaseSum(0)"></sw-number-field>', inject: [ 'repositoryFactory' ], computed: { computedComponentPurchaseSum() { return (i) => { Shopware.State.commit('MyPluginStore/setLoading', ['productComponents', true]) // 引发错误 return 15; } } } })
Store代码(store.js)
... mutations: { setLoading(state, params) { console.log('hello') } }
错误输出
Expected test not to call console.error(). If the error is expected, test for it explicitly by mocking it out using jest.spyOn(console, 'error').mockImplementation() and test that the warning occurs. [Vue warn]: You may have an infinite update loop in a component render function. found in ---> <MyComponent> <Root> at console.captureMessage [as error] ([...]/node_modules/jest-fail-on-console/index.js:56:25) at warn ([...]/node_modules/vue/dist/vue.runtime.common.dev.js:621:15) at flushSchedulerQueue ([...]/node_modules/vue/dist/vue.runtime.common.dev.js:4319:9) at Array.<anonymous> ([...]/node_modules/vue/dist/vue.runtime.common.dev.js:1985:12) at flushCallbacks ([...]/node_modules/vue/dist/vue.runtime.common.dev.js:1911:14) at processTicksAndRejections (node:internal/process/task_queues:95:5) at flushUnexpectedConsoleCalls ([...]/node_modules/jest-fail-on-console/index.js:40:13) at Object.<anonymous> ([...]/node_modules/jest-fail-on-console/index.js:95:7)
问题解答
1. 如何调试该错误?
- 在计算属性内部、mutation方法中添加
console.log,打印调用次数,确认是否存在循环执行 - 用Vue DevTools查看组件更新触发记录,追踪每次更新的触发源
- 临时移除commit调用,验证测试是否恢复正常,确认commit是否为循环触发点
- 在测试中用
jest.spyOn(Shopware.State, 'commit')监听commit调用次数,确认是否被反复触发
2. 错误产生的原因是什么?
核心问题是在渲染阶段调用了Vuex的mutation:
- 组件模板中的
computedComponentPurchaseSum(0)会在渲染时执行,而该函数内部调用Shopware.State.commit触发了Vuex状态变更 - Vue检测到状态变更后,会触发组件重新渲染;重新渲染时又会再次执行该计算属性函数,再次调用commit,形成无限循环
- 开发环境未报错,可能是因为开发环境的更新调度机制与测试环境存在差异,或者循环被Vue内部限制打断,但测试环境的Jest和Vue Test Utils对这类循环的检测更严格
3. 如何修复该问题?
核心原则是不要在渲染/计算属性中修改状态,可通过以下方案修复:
方案一:将状态变更移至生命周期钩子
如果loading状态需要在组件初始化时设置,放到created或mounted钩子中:
Component.register('my-component', { template: '<sw-number-field :value="computedComponentPurchaseSum(0)"></sw-number-field>', inject: ['repositoryFactory'], created() { Shopware.State.commit('MyPluginStore/setLoading', ['productComponents', true]) }, computed: { computedComponentPurchaseSum() { return (i) => { return 15; } } } })
方案二:将计算属性改为方法
如果状态变更需要在特定时机触发(如用户交互),把计算属性改为方法,仅在需要时调用:
Component.register('my-component', { template: '<sw-number-field :value="componentPurchaseSum"></sw-number-field>', inject: ['repositoryFactory'], data() { return { componentPurchaseSum: 0 } }, methods: { fetchComponentPurchaseSum(i) { Shopware.State.commit('MyPluginStore/setLoading', ['productComponents', true]) this.componentPurchaseSum = 15; } }, mounted() { this.fetchComponentPurchaseSum(0); } })
方案三:测试时mock掉commit方法
如果仅需测试组件渲染逻辑,无需验证状态变更,可在测试中mockShopware.State.commit:
jest.spyOn(Shopware.State, 'commit').mockImplementation(() => {}); // 后续执行组件测试逻辑
内容的提问来源于stack exchange,提问作者rammi22
相关产品推荐
相关产品推荐

