如何使用react-testing-library/jest测试基于matchMedia的React响应式状态逻辑
如何测试基于matchMedia的React响应式状态逻辑
我来帮你搞定这个测试难题!你的当前测试代码有几个关键问题:mock的matchMedia没有正确保存监听回调,事件派发的方式不对,还有期望的文本内容也写错了。咱们一步步修正,写出能正确验证响应式逻辑的测试用例。
核心问题分析
你的测试里,window.matchMedia的mock只是返回了一个空的addEventListener jest函数,但没有实际保存我们添加的回调函数,所以后续没法触发这些回调来模拟窗口尺寸变化。另外,直接全局派发change事件也不会触发matchMedia的监听逻辑,得针对具体的媒体查询对象触发事件。
正确的测试方案
我们需要改造matchMedia的mock,让它能记录每个媒体查询对应的监听回调,然后在测试时手动触发这些回调来模拟窗口尺寸变化。
完整测试代码示例
// 先创建一个工具函数来mock matchMedia,保存每个query的监听回调 const mockMatchMedia = () => { const mediaQueryListeners = new Map(); window.matchMedia = jest.fn((query) => { // 根据当前窗口宽度判断初始matches值 const getInitialMatches = () => { switch(query) { case '(max-width: 1012px)': return globalThis.innerWidth <= 1012; case '(max-width: 1460px)': return globalThis.innerWidth <= 1460; case '(min-width: 1460px)': return globalThis.innerWidth >= 1460; default: return false; } }; return { matches: getInitialMatches(), media: query, addEventListener: (event, listener) => { // 保存每个query对应的listener if (!mediaQueryListeners.has(query)) { mediaQueryListeners.set(query, []); } mediaQueryListeners.get(query).push(listener); }, removeEventListener: jest.fn(), // 模拟触发change事件的方法 triggerChange: (newMatches) => { const listeners = mediaQueryListeners.get(query) || []; listeners.forEach(listener => { listener({ matches: newMatches }); }); } }; }); }; test('should update item state correctly on window resize', () => { // 初始化mock mockMatchMedia(); // 测试1: 初始宽度1500px(大于1460) globalThis.innerWidth = 1500; renderTestComponent(); expect(screen.getByTestId('item')).toHaveTextContent('both'); // 测试2: 缩小到1000px(小于1012),触发(max-width:1012px)的change事件 const smallScreenQuery = window.matchMedia('(max-width: 1012px)'); smallScreenQuery.triggerChange(true); // 模拟匹配该查询 expect(screen.getByTestId('item')).toHaveTextContent('signup'); // 测试3: 放大到1200px(介于1012和1460之间),触发(max-width:1460px)的change事件 const mediumScreenQuery = window.matchMedia('(max-width: 1460px)'); mediumScreenQuery.triggerChange(true); // 模拟匹配该查询 expect(screen.getByTestId('item')).toHaveTextContent('info'); // 测试4: 再放大到1500px,触发(min-width:1460px)的change事件 const largeScreenQuery = window.matchMedia('(min-width: 1460px)'); largeScreenQuery.triggerChange(true); // 模拟匹配该查询 expect(screen.getByTestId('item')).toHaveTextContent('both'); });
代码解释
mockMatchMedia函数:- 使用
Map存储每个媒体查询对应的监听回调,这样我们能精准触发对应查询的事件 - 每个返回的媒体查询对象添加了
triggerChange方法,用来手动触发change事件并传递新的matches状态 - 初始的
matches值会根据当前innerWidth自动计算,保证初始状态正确
- 使用
测试步骤:
- 先设置初始窗口宽度,渲染组件后验证初始状态
- 针对每个尺寸变化场景,获取对应的媒体查询对象,调用
triggerChange模拟匹配状态变化,然后验证组件状态更新
你当前测试的错误点修正
- 之前的
dispatchEvent(new Event('change'))是全局派发事件,不会触发matchMedia的监听逻辑,现在改成针对具体查询对象触发回调 - 期望的文本内容写错了(你写的是
'box',实际应该是'signup') - 之前的mock没有处理不同查询的初始
matches值,现在的mock会根据窗口宽度自动计算初始状态
这样写出来的测试就能完整覆盖你的响应式逻辑啦!
内容的提问来源于stack exchange,提问作者Sia
相关产品推荐
相关产品推荐

