如何在Jasmine中mock rxjs的webSocket,解决其不可配置的报错问题
问题原因
报错 Error: <spyOnProperty> : webSocket is not declared configurable 的根本原因是:RxJS 6.5.5 中 rxjs/webSocket 导出的 webSocket 是普通函数,不是属性访问器,且ESM规范下的模块导出成员默认是不可配置的,无法通过 spyOnProperty 修改其属性描述符。
解决方案1:模块级Mock(无需修改业务代码)
如果使用Jest作为测试框架,直接对整个rxjs/webSocket模块进行Mock即可,测试代码修改如下:
import * as rxJsWebSocket from 'rxjs/webSocket'; import { Subject } from 'rxjs'; // Mock整个模块,保留原有其他导出,替换webSocket为可打桩的函数 jest.mock('rxjs/webSocket', () => ({ ...jest.requireActual('rxjs/webSocket'), webSocket: jest.fn(), })); describe('ChatConnectionService', () => { let service: ChatConnectionService; const mockSocketSubject = new Subject(); beforeEach(() => { // 给webSocket函数打桩返回我们的模拟Subject (rxJsWebSocket.webSocket as jest.Mock).mockReturnValue(mockSocketSubject as any); service = new ChatConnectionService(); }); it('should create a new connection when service instantiated', () => { expect(rxJsWebSocket.webSocket).toHaveBeenCalledTimes(1); expect(rxJsWebSocket.webSocket).toHaveBeenCalledWith('.../chat'); }); });
如果使用Jasmine作为测试框架,可以用Jasmine对应的模块mock工具,或者改用下面的依赖注入方案。
解决方案2:依赖注入优化(更符合可测试性设计)
只需要对业务代码做小幅调整,将webSocket工厂作为可选依赖注入,完全不需要修改模块导出就能实现测试:
调整后的业务代码
import {webSocket, WebSocketSubject} from 'rxjs/webSocket'; import {delayWhen, retryWhen, take} from 'rxjs/operators'; import {timer, Observable} from 'rxjs'; // 定义webSocket工厂类型 type WebSocketFactory = typeof webSocket; export class ChatConnectionService { private readonly _connection: WebSocketSubject<any>; public readonly messages$: Observable<ChatInboundMessage>; // 增加可选的工厂参数,生产环境不传就用原生webSocket constructor(webSocketFactory: WebSocketFactory = webSocket) { this._connection = this.getConnection(webSocketFactory); this.messages$ = this._connection.asObservable(); } private getConnection(webSocketFactory: WebSocketFactory): WebSocketSubject<any> { return webSocketFactory(`.../chat`); } }
对应的测试代码
import { Subject } from 'rxjs'; describe('ChatConnectionService', () => { let service: ChatConnectionService; const mockSocketSubject = new Subject(); // 直接创建mock工厂函数 const mockWebSocketFactory = jest.fn(() => mockSocketSubject as any); beforeEach(() => { service = new ChatConnectionService(mockWebSocketFactory); }); it('should create a new connection when service instantiated', () => { expect(mockWebSocketFactory).toHaveBeenCalledTimes(1); expect(mockWebSocketFactory).toHaveBeenCalledWith('.../chat'); }); });
这个方案不需要依赖测试框架的模块Mock能力,耦合度更低,也更符合面向对象的可测试性设计原则。
内容的提问来源于stack exchange,提问作者Majesty
相关产品推荐
相关产品推荐

