You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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');
});

代码解释

  1. mockMatchMedia函数:

    • 使用Map存储每个媒体查询对应的监听回调,这样我们能精准触发对应查询的事件
    • 每个返回的媒体查询对象添加了triggerChange方法,用来手动触发change事件并传递新的matches状态
    • 初始的matches值会根据当前innerWidth自动计算,保证初始状态正确
  2. 测试步骤:

    • 先设置初始窗口宽度,渲染组件后验证初始状态
    • 针对每个尺寸变化场景,获取对应的媒体查询对象,调用triggerChange模拟匹配状态变化,然后验证组件状态更新

你当前测试的错误点修正

  • 之前的dispatchEvent(new Event('change'))是全局派发事件,不会触发matchMedia的监听逻辑,现在改成针对具体查询对象触发回调
  • 期望的文本内容写错了(你写的是'box',实际应该是'signup')
  • 之前的mock没有处理不同查询的初始matches值,现在的mock会根据窗口宽度自动计算初始状态

这样写出来的测试就能完整覆盖你的响应式逻辑啦!

内容的提问来源于stack exchange,提问作者Sia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 21:19:11