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

Cypress监视React组件调用的外部函数失败及网络测试疑难咨询

一、为什么监视独立文件的函数失效?

你测试代码里导入的func和应用运行时的func不是同一个实例——测试代码在Cypress的Node环境执行,而应用代码是打包后在浏览器环境运行,两者的模块实例完全隔离。你给测试环境的func.call加了spy,但应用里调用的是浏览器环境里的那个,自然检测不到。

解决方法:

  • 把func挂载到浏览器的window对象上,这样测试可以通过cy.window()获取到应用里的实例:
    在你的test.js里添加:
    export const func = {
      call: (e) => console.log(e),
    };
    // 仅在测试环境暴露到window
    if (process.env.NODE_ENV === 'test') {
      window.testFunc = func;
    }
    
    然后在测试里:
    describe("template spec", () => {
      it("calls func.call", () => {    
        cy.visit("/");
        cy.window().then(win => {
          cy.spy(win.testFunc, "call").as('funcCall'); // 用as命名方便后续断言
        });
        // 触发调用func.call的操作
        cy.get('[data-testid="trigger-button"]').click();
        cy.get('@funcCall').should('have.been.called');
      });
    });
    
  • 或者用Cypress的cy.stub直接替换应用里的函数实现,通过修改全局对象或者模块依赖(如果用webpack等打包工具,可通过注入替换模块)。

二、如何测试Socket.io流量?

方法1:监视浏览器里的Socket实例

如果应用里的Socket.io客户端挂载到了window,可以直接监听它的emit/on方法:

cy.visit('/');
cy.window().then(win => {
  // 监视socket.emit调用
  cy.spy(win.socket, 'emit').as('socketEmit');
  // 监视socket.on接收事件
  cy.spy(win.socket, 'on').as('socketOn');
});
// 触发Socket操作
cy.get('[data-testid="send-socket-event"]').click();
// 断言emit被调用,且参数正确
cy.get('@socketEmit').should('have.been.calledWith', 'message', 'hello world');

方法2:拦截Socket.io的HTTP请求

Socket.io初始握手是HTTP请求,后续用WebSocket传输,你可以用cy.intercept拦截握手请求,或者监听WebSocket帧(Cypress 9+支持WebSocket拦截):

// 拦截Socket.io的握手请求
cy.intercept('GET', '/socket.io/?EIO=4&transport=polling*').as('socketHandshake');
cy.visit('/');
cy.wait('@socketHandshake');

// 拦截WebSocket帧(Cypress 9+)
cy.intercept('ws', 'ws://localhost:3000/socket.io/*').as('socketWs');
cy.get('@socketWs').then(interception => {
  // 查看WebSocket的发送/接收数据
  console.log(interception.messages);
});

方法3:启动本地mock Socket服务

在测试前启动一个简单的Socket.io服务,让应用连接到这个mock服务,然后验证双向通信:

// 在测试文件顶部启动mock服务
const { Server } = require('socket.io');
let io;

before(() => {
  io = new Server(3001);
  io.on('connection', (socket) => {
    socket.on('message', (data) => {
      socket.emit('reply', `received: ${data}`);
    });
  });
});

after(() => {
  io.close();
});

it('tests socket communication', () => {
  // 确保应用连接到mock服务(可通过环境变量配置)
  cy.visit('/', {
    onBeforeLoad: (win) => {
      win.SOCKET_URL = 'ws://localhost:3001';
    }
  });
  cy.window().then(win => {
    cy.spy(win.socket, 'on').as('socketOn');
  });
  cy.get('[data-testid="send-message"]').click();
  cy.get('@socketOn').should('have.been.calledWith', 'reply', 'received: hello');
});

三、如何获取fetch的原始请求体(包括FormData)?

cy.intercept里的req.body如果是FormData,会被序列化为字符串(比如multipart/form-data格式的字符串),无法直接读取。你需要用req.formData()方法异步解析:

cy.intercept('POST', '/api/upload', async (req) => {
  // 解析FormData
  const formData = await req.formData();
  // 获取FormData里的字段
  const username = formData.get('username');
  const file = formData.get('avatar');
  // 断言字段正确
  expect(username).to.equal('test-user');
  expect(file.name).to.equal('avatar.jpg');
  // 发送mock响应
  req.reply({ statusCode: 200, body: { success: true } });
}).as('uploadRequest');

// 触发上传操作
cy.get('[data-testid="upload-button"]').click();
cy.wait('@uploadRequest');

如果是普通JSON请求,直接用req.body即可;如果是application/x-www-form-urlencoded格式,可用req.body解析为对象,或用req.formData()获取。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 22:15:20