如何模拟服务器发送事件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
相关产品推荐
相关产品推荐

