如何使用React Testing Library测试依赖媒体查询的响应式React组件
问题原因说明
这个问题不是React Testing Library(RTL)特有,本质是Jest默认使用的jsdom运行环境的限制:
- react-responsive底层依赖浏览器标准
window.matchMediaAPI执行媒体查询匹配,而非直接读取window.innerWidth判断 - jsdom的
matchMedia默认实现不会根据当前视口尺寸动态计算查询结果,不管你设置的innerWidth是多少,所有媒体查询默认都会返回不匹配,所以才会走到兜底渲染逻辑。
解决方案
你需要对useMediaQuery进行mock,或者模拟matchMedia的实现,两种常用方案如下:
方案1:直接mock react-responsive的useMediaQuery hook(更简单,推荐)
直接在测试用例中控制不同媒体查询的返回值,不需要关心底层API实现:
// test spec 修改后 import React from 'react' import Test from './MediaQueryTest' import { render, screen } from '@testing-library/react' import { useMediaQuery } from 'react-responsive' // 全局mock react-responsive库 jest.mock('react-responsive', () => ({ useMediaQuery: jest.fn(), })) it('should render for desktop', () => { // 针对当前测试用例设置返回值:桌面查询匹配,移动端查询不匹配 useMediaQuery.mockImplementation(({ query }) => { const DESKTOP_MEDIA_QUERY = 'only screen and (min-width: 1024px)' const MOBILE_MEDIA_QUERY = 'only screen and (max-width : 480px)' if (query === DESKTOP_MEDIA_QUERY) return true if (query === MOBILE_MEDIA_QUERY) return false return false }) render(<Test/>) expect(screen.getByText('desktop')).toBeInTheDocument() }) // 移动端测试同理 it('should render for mobile', () => { useMediaQuery.mockImplementation(({ query }) => { const DESKTOP_MEDIA_QUERY = 'only screen and (min-width: 1024px)' const MOBILE_MEDIA_QUERY = 'only screen and (max-width : 480px)' if (query === DESKTOP_MEDIA_QUERY) return false if (query === MOBILE_MEDIA_QUERY) return true return false }) render(<Test/>) expect(screen.getByText('mobile')).toBeInTheDocument() })
注意:mock后组件渲染逻辑完全同步,不需要使用findBy这类异步查询方法,直接用getBy即可。
方案2:mock window.matchMedia (更贴近真实运行逻辑)
如果需要模拟修改视口宽度触发媒体查询变化的场景,可以直接模拟matchMedia的实现:
// 可以放在测试 setup 文件里全局生效 const setMatchMediaMock = (width) => { window.matchMedia = jest.fn().mockImplementation(query => { // 简单解析媒体查询的宽限规则,可根据自己的查询规则扩展 const ruleMatch = query.match(/\((min|max)-width\s*:\s*(\d+)px\)/) let matches = false if (ruleMatch) { const [, type, val] = ruleMatch matches = type === 'min' ? width >= +val : width <= +val } return { matches, addListener: jest.fn(), removeListener: jest.fn(), } }) } // 测试用例中使用 it('should render for desktop', () => { setMatchMediaMock(1024) // 设置视口宽度为1024px render(<Test/>) expect(screen.getByText('desktop')).toBeInTheDocument() })
内容的提问来源于stack exchange,提问作者Neil Girardi
相关产品推荐
相关产品推荐

