如何为CarCard组件的模态框可见性编写单元测试?
问题:CarCard组件模态框单元测试异常处理
我有一个展示车辆信息的CarCard组件,鼠标悬停后点击“查看更多”按钮会弹出CarDetails模态框显示详情。编写单元测试验证点击按钮时模态框是否正常打开,但当前测试中modalElement为null;如果在userEvent.click前添加await,会抛出错误:Error: Uncaught [ReferenceError: ResizeObserver is not defined]。
CarCard组件代码
const CarCard: React.FC<CarCardProps> = ({ car }) => { const { city_mpg, year, make, model, transmission, drive } = car; const [isOpen, setIsOpen] = useState(false); const carRent = calculateCarRent(city_mpg, year); return ( <div className="car-card group"> <div className="car-card__content"> <h2 className="car-card__content-title"> {make} {model} </h2> </div> <p className="flex mt-6 text-[32px] font-extrabold"> <span className="self-start text-[14px] font-semibold">$</span> {carRent} <span className="self-end text-[14px] font-medium">/day</span> </p> <div className="relative w-full h-40 my-3 object-contain"> <Image src={generateCarImageUrl(car)} alt="car model" fill priority className="object-contain" /> </div> <div className="relative flex w-full mt-2"> <div className="flex group-hover:invisible w-full justify-between text-gray"> <div className="flex flex-col justify-center items-center gap-2"> <Image src="/steering-wheel.svg" width={20} height={20} alt="steering wheel" /> <p className="text-[14px]"> {transmission === "a" ? "Automatic" : "Manual"} </p> </div> <div className="flex flex-col justify-center items-center gap-2"> <Image src="/tire.svg" width={20} height={20} alt="tire" /> <p className="text-[14px]">{drive.toUpperCase()}</p> </div> <div className="flex flex-col justify-center items-center gap-2"> <Image src="/gas.svg" width={20} height={20} alt="gas" className="w-auto h-auto" /> <p className="text-[14px]">{city_mpg} MPG</p> </div> </div> <div className="car-card__btn-container"> <CustomButton title="View More" containerStyles="w-full py-[16px] rounded-full bg-primary-blue" textStyles="text-white text-[14px] font-bold" rightIcon="/right-arrow.svg" handleClick={() => setIsOpen(true)} /> </div> </div> <CarDetails isOpen={isOpen} closeModal={() => setIsOpen(false)} car={car} /> </div> ); }; export default CarCard;
CarDetails模态框代码
const CarDetails: React.FC<CarDetailsProps> = ({ isOpen, closeModal, car }) => ( <> <Transition appear show={isOpen} as={Fragment} data-testid="modal"> <Dialog as="div" className="relative z-10" onClose={closeModal}> <Transition.Child as={Fragment} enter="ease-out duration-300" enterFrom="opacity-0" enterTo="opacity-100" leave="ease-in duration-200" leaveFrom="opacity-100" leaveTo="opacity-0" > <div className="fixed inset-0 bg-black bg-opacity-25" /> </Transition.Child> <div className="fixed inset-0 overflow-y-auto"> <div className="flex min-h-full items-center justify-center p-4 text-center"> <Transition.Child as={Fragment} enter="ease-out duration-300" enterFrom="opacity-0 scale-95" enterTo="opacity-100 scale-100" leave="ease-out duration-300" leaveFrom="opacity-100 scale-100" leaveTo="opacity-0 scale-95" > <Dialog.Panel className="relative w-full max-w-lg max-h-[90vh] overflow-y-auto transform rounded-2xl bg-white p-6 text-left shadow-xl transition-all flex flex-col gap-5"> <button type="button" className="absolute top-2 right-2 z-10 w-fit p-2 bg-primary-blue-100 rounded-full" onClick={closeModal} > <Image src="/close.svg" alt="close" width={20} height={20} className="object-contain" /> </button> <div className="flex-1 flex flex-col gap-3"> <div className="relative w-full h-40 bg-pattern bg-cover bg-center rounded-lg"> <Image src={generateCarImageUrl(car)} alt="car model" fill priority className="object-contain" /> </div> <div className="flex gap-3"> <div className="flex-1 relative w-full h-24 bg-primary-blue-100 rounded-lg"> <Image src={generateCarImageUrl(car, "29")} alt="car model" fill priority className="object-contain" /> </div> <div className="flex-1 relative w-full h-24 bg-primary-blue-100 rounded-lg"> <Image src={generateCarImageUrl(car, "33")} alt="car model" fill priority className="object-contain" /> </div> <div className="flex-1 relative w-full h-24 bg-primary-blue-100 rounded-lg"> <Image src={generateCarImageUrl(car, "13")} alt="car model" fill priority className="object-contain" /> </div> </div> </div> <div className="flex-1 flex flex-col gap-2"> <h2 className="font-semibold text-xl capitalize"> {car.make} {car.model} </h2> <div className="mt-3 flex flex-wrap gap-4"> {Object.entries(car).map(([key, value]) => ( <div className="flex justify-between gap-5 w-full text-right" key={key} > <h4 className="text-grey capitalize"> {key.split("_").join(" ")} </h4> <p className="text-black-100 font-semibold">{value}</p> </div> ))} </div> </div> </Dialog.Panel> </Transition.Child> </div> </div> </Dialog> </Transition> </> ); export default CarDetails;
原单元测试代码
it("opens car details modal when 'View More' button is clicked", () => { render(<CarCard car={mockCar} />); const viewMoreBtn = screen.getByText("View More"); userEvent.click(viewMoreBtn); const modalElement = screen.queryByTestId("modal"); expect(modalElement).toBeInTheDocument(); });
解决方案
1. 处理模态框未找到的问题
CarDetails使用了Transition组件,模态框显示是带动画的异步过程,直接点击后立即查询元素会因为React状态还没更新完成而找不到。需要等待UI更新完成:
修改测试代码,使用await等待点击操作完成,配合getByTestId(会等待元素出现):
it("点击'查看更多'按钮打开车辆详情模态框", async () => { render(<CarCard car={mockCar} />); const viewMoreBtn = screen.getByText("View More"); await userEvent.click(viewMoreBtn); const modalElement = screen.getByTestId("modal"); expect(modalElement).toBeInTheDocument(); });
如果还是不稳定,可以用waitFor确保元素渲染完成:
it("点击'查看更多'按钮打开车辆详情模态框", async () => { render(<CarCard car={mockCar} />); const viewMoreBtn = screen.getByText("View More"); await userEvent.click(viewMoreBtn); await waitFor(() => { expect(screen.getByTestId("modal")).toBeInTheDocument(); }); });
2. 解决ResizeObserver未定义的问题
测试环境(如Jest)没有内置浏览器的ResizeObserverAPI,需要手动模拟或引入polyfill:
方式一:手动模拟
在测试文件顶部或测试全局setup文件中添加:
global.ResizeObserver = class ResizeObserver { constructor(callback) {} observe(target) {} unobserve(target) {} disconnect() {} };
方式二:使用polyfill
安装resize-observer-polyfill:
npm install resize-observer-polyfill --save-dev
然后在测试setup文件中导入:
import 'resize-observer-polyfill';
内容的提问来源于stack exchange,提问作者Enes
相关产品推荐
相关产品推荐

