Shopware 6中使用Jest与vue-test-utils测试组件时的渲染异常问题排查
Shopware 6中使用Jest与vue-test-utils测试组件时的渲染异常问题排查
嘿,我来帮你拆解这个问题~
首先咱们先搞懂为什么await wrapper.setData({}).then(() => {})能让组件正确渲染:
其实这是个“歪打正着”的hack——你调用setData({})的时候并没有修改任何数据,但这个操作会强制触发Vue的响应式更新周期,让组件有时间完成所有依赖状态的异步渲染逻辑(比如等待Vuex状态同步、异步请求返回数据),所以原本显示骨架屏的组件,在这次强制更新后就加载出了正确内容。
而你没加这行代码时组件只显示骨架屏,核心原因是异步渲染的时机没跟上:
- Vue的响应式更新本身是异步的,当你提交Vuex mutation后,组件不会立刻渲染新状态,得等下一个渲染周期
- 你的组件还依赖了异步请求(比如测试里mock的
repositoryFactory.get('unit')返回的Promise),组件mount后这些异步操作还在pending状态,所以先显示了骨架屏占位
接下来给你几个更规范的解决方案,替代hack式的setData:
方案1:用$nextTick等待DOM更新
这是Vue官方推荐的等待渲染完成的方式,专门用来等待下一次DOM更新周期:
// 替换掉原来的await wrapper.setData({}).then(() => {}) await wrapper.vm.$nextTick();
方案2:结合flushPromises处理异步请求
如果组件依赖了异步接口(比如你测试里获取unit数据的逻辑),可以先清空所有pending的Promise,再等待DOM更新:
// 先引入flushPromises(如果没安装,可自己实现) import flushPromises from '@vue/test-utils/dist/flushPromises'; // 在mount组件后执行 await flushPromises(); // 等待所有异步请求完成 await wrapper.vm.$nextTick(); // 等待DOM更新完成
如果不想额外引入依赖,也可以自己简单实现flushPromises:
const flushPromises = () => new Promise(resolve => setImmediate(resolve));
针对你的测试代码的修改示例
把测试用例里的触发更新部分换成规范写法:
it('should update list', async () => { // ... 省略状态提交、组件mount的代码 ... // 先等待所有异步请求完成,再等待DOM更新 await flushPromises(); await wrapper.vm.$nextTick(); console.log(wrapper.html()) expect(wrapper.findAll('fieldset').length).toEqual(1); });
为什么这比setData更好?
setData({})本质是无意义的触发更新,属于临时的hack;而flushPromises+$nextTick是顺着Vue的渲染逻辑来的,能准确等待异步操作和DOM更新完成,测试逻辑更可靠,也更容易维护。
备注:内容来源于stack exchange,提问作者rammi22
相关产品推荐
相关产品推荐

