如何使用Vitest测试SolidJS Portal组件生成的弹窗内容?
SolidJS Portal选择器弹窗的Vitest测试方案
问题背景
基于SolidJS的Portal组件实现了自定义选择器弹窗,目标是用Vitest覆盖弹窗内容的测试。最初直接渲染根组件后,触发弹窗打开按钮的点击事件,发现文档中并未生成预期的弹窗DIV(已确认点击事件触发成功)。尝试Mock Portal组件时,又遇到props.children始终为空的问题,无法测试由my_json_objects_collection生成的弹窗内容结构与数据。
高效测试方案
1. 直接定位Portal的实际挂载目标
SolidJS的Portal默认会把内容挂载到document.body,而非测试的默认容器里。测试时直接查询document.body即可找到弹窗内容,无需提前Mock:
import { render, screen, fireEvent } from '@testing-library/solid'; test('弹窗内容正确渲染', () => { const mockOptions = [{ label: '股票' }, { label: '债券' }]; render(() => ( <ComponentRoot options={mockOptions}> <PopperOpenerComponent /> <Portal> {/* 由options生成的列表内容 */} </Portal> </ComponentRoot> )); // 触发弹窗打开 fireEvent.click(screen.getByRole('button')); // 直接在body中查询弹窗内容 expect(screen.getByText('股票')).toBeInTheDocument(); expect(screen.getAllByRole('listitem')).toHaveLength(2); });
2. 修复Portal的Mock逻辑
如果必须Mock Portal(比如避免真实DOM挂载),需要正确处理SolidJS的Children信号,不要手动解析子元素,直接把原组件生成的子内容渲染到测试容器中:
import { vi } from 'vitest'; import type { ParentProps } from 'solid-js'; vi.mock('solid-js/web', async () => { const actual = await vi.importActual('solid-js/web'); return { ...actual, // 把Portal内容直接渲染到带标识的容器中,方便查询 Portal: (props: ParentProps) => ( <div data-testid="portal-container">{props.children}</div> ) }; }); // 测试用例 test('Mock Portal后内容正确', () => { const mockOptions = [{ label: '黄金' }, { label: '原油' }]; render(() => ( <ComponentRoot options={mockOptions}> <PopperOpenerComponent /> <Portal> {/* 由options生成的列表内容 */} </Portal> </ComponentRoot> )); fireEvent.click(screen.getByRole('button')); const portal = screen.getByTestId('portal-container'); expect(portal).toHaveTextContent('黄金'); expect(portal.querySelectorAll('li')).toHaveLength(2); });
3. 单独测试内容生成逻辑
如果弹窗内容是由独立的渲染函数生成的,可以抽离该函数单独测试,无需依赖Portal和弹窗组件:
// 假设内容生成函数是generateOptionsList import { generateOptionsList } from './your-component-file'; test('内容生成函数输出正确结构', () => { const mockOptions = [{ label: '基金', value: 'fund' }, { label: '衍生品', value: 'deriv' }]; // 渲染生成的列表 const { container } = render(() => generateOptionsList(mockOptions)); expect(container.querySelectorAll('li')).toHaveLength(2); expect(container.querySelector('li[data-value="fund"]')?.textContent).toBe('基金'); });
内容的提问来源于stack exchange,提问作者Foxlab
相关产品推荐
相关产品推荐

