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

如何用Jest测试含第三方库只读字段(如ws的readyState)的代码

测试含第三方库只读字段的WebSocket代码解决方案

问题场景

我需要用Jest测试以下基于ws库的WebSocket连接代码,核心是验证重复连接时是否抛出指定错误。但测试过程中发现WebSocket实例的readyState是只读属性,无法直接修改,导致测试无法推进。

待测试代码(Socket.js)

import WebSocket from "ws";

export default class Socket {
    socket = undefined;

    connect(url): void {
        if (this.socket !== undefined && this.socket.readyState === WebSocket.OPEN) {
            throw new Error("Socket is already connected");
        }

        this.socket = new WebSocket(url);
    }
}

测试代码及问题

我尝试Mock ws库,但修改readyState时遇到TypeScript报错:无法赋值给'readyState',因为它是只读属性。ts(2540)

测试代码如下:

import Socket from "./Socket";
import WebSocket from "ws";

jest.mock("ws");

it("can connect", () => {
    // Arrange
    const url = "www.someurl.com";
    jest.spyOn(global, "Promise").mockReturnValueOnce({} as Promise<void>);

    const sut = new Socket();

    // Act
    sut.connect(url);

    const webSocket = jest.mocked(WebSocket).mock.instances[0];

    // Set readystate of socket
    webSocket.readyState = WebSocket.CLOSED; // 这里报错:readyState是只读属性

    const action = () => sut.connect(url);

    // Assert
    expect(action).toThrowError(new Error("Socket is already connected"));
});

解决方案

针对只读属性的测试需求,有三种可行的处理方式:

方式1:在Mock实例初始化时直接定义readyState

修改Jest对ws的Mock逻辑,让创建实例时直接带上指定的readyState值:

import Socket from "./Socket";
import WebSocket from "ws";

jest.mock("ws", () => {
    // 自定义Mock构造函数,初始化时就设置readyState
    return jest.fn().mockImplementation(() => ({
        readyState: WebSocket.OPEN // 直接设置为OPEN状态,满足触发错误的条件
    }));
});

it("throws error when connecting twice with open socket", () => {
    const url = "www.someurl.com";
    const sut = new Socket();

    // 第一次连接,创建mock实例(readyState为OPEN)
    sut.connect(url);

    // 第二次连接应该抛出错误
    expect(() => sut.connect(url)).toThrowError("Socket is already connected");
});

方式2:用Object.defineProperty修改属性的可写性

如果需要在测试过程中动态修改readyState,可以用Object.defineProperty重新定义该属性,开启可写权限:

import Socket from "./Socket";
import WebSocket from "ws";

jest.mock("ws");

it("throws error when connecting twice with open socket", () => {
    const url = "www.someurl.com";
    const sut = new Socket();

    sut.connect(url);
    const webSocket = jest.mocked(WebSocket).mock.instances[0];

    // 重新定义readyState为可写属性,并设置为OPEN
    Object.defineProperty(webSocket, "readyState", {
        value: WebSocket.OPEN,
        writable: true // 允许后续修改(如果需要)
    });

    expect(() => sut.connect(url)).toThrowError("Socket is already connected");
});

方式3:模拟readyState的getter方法

通过jest.spyOn模拟readyState的getter,动态返回需要的值:

import Socket from "./Socket";
import WebSocket from "ws";

jest.mock("ws");

it("throws error when connecting twice with open socket", () => {
    const url = "www.someurl.com";
    const sut = new Socket();

    sut.connect(url);
    const webSocket = jest.mocked(WebSocket).mock.instances[0];

    // 模拟readyState的getter,返回OPEN状态
    jest.spyOn(webSocket, "readyState", "get").mockReturnValue(WebSocket.OPEN);

    expect(() => sut.connect(url)).toThrowError("Socket is already connected");
});

说明

三种方式中,方式1最简洁,适合只需要固定状态的测试场景;方式2和3更灵活,适合需要在测试过程中动态切换状态的场景,可根据测试需求选择。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 18:15:48