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

如何模拟服务器发送事件SSE(EventSource)?AngularJS下Protractor实现

嘿,我来帮你搞定Protractor模拟SSE事件的问题!

首先得明确:Angular的$httpBackend没法直接处理SSE请求,因为EventSource是浏览器原生API,它发起的请求不走Angular的$http管道,所以$httpBackend拦截不到这类请求。咱们得换个思路,直接在浏览器上下文里mockEventSource或者手动触发事件监听逻辑。

方案一:Mock浏览器原生的EventSource对象

在测试前,我们可以通过Protractor的executeScript向浏览器注入一个模拟的EventSource类,这样应用里创建的EventSource实例就是我们可控的模拟对象,随时能触发指定事件。

步骤1:在测试前置环节注入Mock

beforeEach(async () => {
  await browser.executeScript(() => {
    // 先保存原生EventSource,方便测试后恢复
    window.nativeEventSource = window.EventSource;

    // 自定义模拟的EventSource类
    window.EventSource = function(url) {
      this.url = url;
      // 存储事件监听函数
      this.listeners = {};

      // 模拟addEventListener方法
      this.addEventListener = (eventName, callback) => {
        if (!this.listeners[eventName]) {
          this.listeners[eventName] = [];
        }
        this.listeners[eventName].push(callback);
      };

      // 模拟close方法
      this.close = () => {
        this.listeners = {};
      };

      // 新增触发事件的方法,供测试调用
      this.triggerEvent = (eventName, data) => {
        if (this.listeners[eventName]) {
          this.listeners[eventName].forEach(callback => {
            // 构造符合SSE事件规范的事件对象
            callback({
              type: eventName,
              data: JSON.stringify(data), // 匹配你业务逻辑里的数据格式
              origin: window.location.origin,
              lastEventId: ""
            });
          });
        }
      };
    };

    // 补充EventSource的静态状态常量
    window.EventSource.CONNECTING = 0;
    window.EventSource.OPEN = 1;
    window.EventSource.CLOSED = 2;
  });
});

步骤2:在测试用例中触发SSE事件

it('should handle the "newuser" SSE event correctly', async () => {
  // 加载应用页面,确保EventSource实例已创建
  await browser.get('/your-app-path');

  // 获取应用里的EventSource实例(根据你的实际情况调整获取方式)
  // 比如如果source是全局变量:
  const mockEventSource = await browser.executeScript(() => window.source);
  // 如果是Angular控制器里的变量,可通过scope获取:
  // const mockEventSource = await browser.executeScript(() => angular.element(document.body).scope().source);

  // 触发"newuser"事件,传入测试数据
  await browser.executeScript((es, userData) => {
    es.triggerEvent('newuser', userData);
  }, mockEventSource, { id: 1001, username: 'TestUser' });

  // 断言业务逻辑执行后的结果,比如页面是否显示了新用户
  const newUserElement = element(by.css('.user-list-item[data-id="1001"]'));
  expect(newUserElement.isDisplayed()).toBe(true);
  expect(newUserElement.getText()).toContain('TestUser');
});

步骤3:测试后恢复原生EventSource

afterEach(async () => {
  await browser.executeScript(() => {
    if (window.nativeEventSource) {
      window.EventSource = window.nativeEventSource;
      delete window.nativeEventSource;
    }
  });
});

方案二:直接触发事件监听函数

如果不想完整mockEventSource,也可以直接找到应用里注册的事件监听函数,手动调用它并传入模拟的事件对象:

it('should trigger the newuser handler directly', async () => {
  await browser.get('/your-app-path');

  // 直接在浏览器上下文触发监听函数
  await browser.executeScript(() => {
    // 构造模拟的SSE事件对象
    const mockEvent = {
      type: 'newuser',
      data: JSON.stringify({ id: 1002, username: 'AnotherTestUser' })
    };

    // 调用监听函数(根据实际情况调整获取方式)
    // 比如如果是全局的监听函数:
    window.source.listeners.newuser[0](mockEvent);
    // 如果是Angular控制器里的方法:
    // const scope = angular.element(document.body).scope();
    // scope.handleNewUser(mockEvent);
    // scope.$apply(); // 若需要触发Angular的脏检查
  });

  // 断言结果
  const newUserElement = element(by.css('.user-list-item[data-id="1002"]'));
  expect(newUserElement.isDisplayed()).toBe(true);
});

关键说明

  • $httpBackend只拦截Angular$http发起的请求,对EventSource这类原生API发起的请求无效,所以必须用浏览器上下文mock的方式。
  • 模拟事件对象时,要尽量贴合真实SSE事件的结构(比如type、data字段),确保业务逻辑能正确解析。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 08:14:36