如何用vue-test-utils+Vitest测试Ionic自定义模态框的内容与行为?
问题:Vue3+Ionic8模态框组件单元测试无法访问内部内容
开发基于Vue3和Ionic8的应用时,自定义模态框组件MyCustomModal.vue使用vue-test-utils和Vitest进行单元测试,无法访问模态框内部的渲染内容:测试代码中wrapper.text()输出空字符串,wrapper.html()仅显示空的<ion-modal>标签,无法找到<h2>元素进行断言。
组件代码
<script setup lang="ts"> const props = defineProps<{ isOpen: boolean; userName: string; }>(); </script> <template> <ion-modal :is-open="isOpen" @did-dismiss="emit('didDismiss')"> <div><h2>Your initial is {{ userName[0] }}</h2></div> </ion-modal> </template>
原测试代码(存在问题)
import { mount } from '@vue/test-utils'; import MyCustomModal from './MyCustomModal.vue'; describe("MyCustomModal", () => { it("should display the title with the name initial", () => { const wrapper = mount(MyCustomModal, { props: { isOpen: true, userName: "John Doe", }, }); expect(wrapper.element.isOpen).toBe(true); // 测试通过 console.log(wrapper.text()); // 输出空字符串 console.log(wrapper.html()); // 输出<ion-modal data-v-30836913=""></ion-modal> expect(wrapper.find("h2").text()).toContain("Your initial is J"); // 报错:Cannot call text on an empty DOMWrapper. }); });
解决方案
Ionic8的ion-modal会将内部内容异步渲染到document.body的独立容器中,而非组件根节点内部,因此测试时需要从全局DOM中获取模态框内容。
1. 访问模态框内容的测试代码
import { mount, flushPromises } from '@vue/test-utils'; import MyCustomModal from './MyCustomModal.vue'; describe("MyCustomModal", () => { // 测试后清理模态框元素,避免影响其他测试 afterEach(() => { document.querySelectorAll('ion-modal').forEach(el => el.remove()); }); it("should display the title with the name initial", async () => { const wrapper = mount(MyCustomModal, { props: { isOpen: true, userName: "John Doe", }, }); // 等待Ionic模态框异步渲染完成 await flushPromises(); // 从全局DOM中获取模态框内容容器 const modalContent = document.querySelector('ion-modal .ion-modal-content'); expect(modalContent).not.toBeNull(); // 将DOM元素包装为vue-test-utils的DOMWrapper,方便查询内部元素 const contentWrapper = wrapper.wrap(modalContent as HTMLElement); expect(contentWrapper.find('h2').text()).toContain("Your initial is J"); }); });
2. 触发模态框事件的测试代码
要测试didDismiss事件的触发,需要直接在模态框DOM元素上分发对应事件:
it("should emit didDismiss when modal is dismissed", async () => { const wrapper = mount(MyCustomModal, { props: { isOpen: true, userName: "John Doe", }, }); await flushPromises(); const modalElement = document.querySelector('ion-modal') as HTMLElement; // 模拟模态框关闭,分发did-dismiss事件 modalElement.dispatchEvent(new CustomEvent('did-dismiss')); // 验证组件是否正确转发事件 expect(wrapper.emitted('didDismiss')).toBeTruthy(); expect(wrapper.emitted('didDismiss')?.length).toBe(1); });
关键注意事项
- 必须使用
await flushPromises()等待Ionic模态框的异步渲染流程完成,否则无法获取到渲染后的内容。 - 测试完成后务必清理全局DOM中的模态框元素,防止测试用例之间产生干扰。
内容的提问来源于stack exchange,提问作者Djizeus
相关产品推荐
相关产品推荐

