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

如何让cy.stub()在React组件测试中对导入模块生效?

React组件测试中useNavigate模拟失效问题排查与解决方向

被测组件代码

// component.tsx
import { useNavigate } from 'react-router-dom';

export default function Component() {
  const navigate = useNavigate();
  // ...
}

测试代码

// component.cy.tsx
import * as reactRouterDom from 'react-router-dom';

cy.stub(reactRouterDom, 'useNavigate')
  .as('useNavigateStub')
  .callsFake(() => {
    console.log(`👾 fake useNavigate`); // <-- 从未执行
  });
console.log(`👾 stub: ${reactRouterDom.useNavigate}`); // 打印原useNavigate函数,而非模拟函数

cy.mount(<Component/>);

问题现状

尝试在Cypress组件测试中模拟useNavigate钩子,但模拟完全未生效:fake函数从未执行,打印的useNavigate仍是原函数。推测问题出在Webpack配置上,曾尝试将ts-loader替换为babel-loader,但程序直接挂起;使用ts-loader时,也找不到能让模拟生效的配置。多数时候无错误反馈,调试困难。

当前Cypress Webpack配置

{
  component: {
    devServer: {
      framework: 'react',
      bundler: 'webpack',
      webpackConfig: {
        entry: './src/index.ts',
        output: {
          path: '<repo>/dist/1.35.6',
          publicPath: 'auto'
        },
        mode: 'development',
        resolve: {
          extensions: [ '.ts', '.tsx', '.js', '.jsx' ],
          alias: {
            '~': '<repo>/src',
            test: '<repo>/test'
          }
        },
        module: {
          rules: [
            {
              test: /\.(js|jsx|ts|tsx)$/,
              loader: 'ts-loader',
              exclude: /node_modules/
            },
            { test: /\.svg/, type: 'asset/inline' },
            { test: /\.png/i, loader: 'file-loader' }
          ]
        },
        plugins: [
          Dotenv {
            config: {
              path: './.env',
              prefix: 'process.env.',
              systemvars: true
            }
          },
          ModuleFederationPlugin {
            // 非常长的配置对象,看似无关紧要
          },
          HtmlWebpackPlugin {
            userOptions: { template: './public/index.html' },
            version: 5
          }
        ],
        devtool: 'cheap-module-source-map',
        devServer: { port: '3003', open: false, historyApiFallback: true }
      }
    }
  }
}

已尝试的调整

主要针对ts-loader规则进行修改:

{
  test: /\.(js|jsx|ts|tsx)$/,
  loader: 'ts-loader',
  exclude: /node_modules/
},

可行解决方向

  • 统一模块实例:Webpack可能对react-router-dom进行了重复打包,导致测试stub的模块和组件引入的不是同一个实例。在webpackConfig的resolve.alias中添加:

    alias: {
      // ...原有别名
      'react-router-dom': require.resolve('react-router-dom')
    }
    

    强制所有引入都指向同一个模块文件。

  • 调整ts-loader的模块编译格式:ts-loader默认可能使用ES模块编译,导致Cypress无法拦截stub。修改tsconfig.json的compilerOptions.module为CommonJS,或者在webpack的ts-loader配置中添加:

    {
      test: /\.(js|jsx|ts|tsx)$/,
      loader: 'ts-loader',
      exclude: /node_modules/,
      options: {
        compilerOptions: {
          module: 'CommonJS'
        }
      }
    }
    
  • 简化测试环境的Webpack配置:移除生产环境的非必要插件(如ModuleFederationPlugin、Dotenv),组件测试不需要这些复杂配置,避免插件干扰模块加载逻辑。

  • 改用官方路由模拟方案:放弃手动stub,在mount组件时包裹<MemoryRouter>,然后通过测试断言导航行为:

    import { MemoryRouter } from 'react-router-dom';
    
    cy.mount(
      <MemoryRouter>
        <Component />
      </MemoryRouter>
    );
    // 后续通过触发组件内的导航操作,断言路由变化
    
  • 调试模块加载路径:在测试文件和被测组件中分别添加console.log(require.resolve('react-router-dom')),对比打印的路径是否一致。如果路径不同,说明模块被多次加载,需要通过resolve配置统一。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 18:22:46