如何使用Jest测试在库的模拟上使用只读字段的代码(例如ws socket.readyState)?

tv6aics1  于 2022-12-08  发布在  Jest
关注(0)|答案(1)|浏览(145)

我想用Jest对这个JS代码进行单元测试。它使用的是ws WebSocket库:
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);
    }
}

我想检查是否抛出了错误。为此,我为套接字创建了一个模拟,然后需要将套接字对象的readyState设置为CLOSED。问题是这是不可能的,因为它是一个只读字段。
测试项目:

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; // Error here: Cannot assign to 'readyState' because it is a read-only property. ts(2540)

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

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

问题:如何在socket.readyState等库的模拟上测试使用只读字段的代码?
**编辑:**根据上述要求添加了mvce。

x33g5p2x

x33g5p2x1#

更好的测试策略是创建一个测试WebSocket服务器并连接它。这也是官方团队正在做的,请参见一些测试用例。不建议模拟ws并测试实现细节。
下面使用TypeScript测试代码:
socket.ts

import WebSocket from "ws";

export default class Socket {
  socket!: WebSocket;

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

socket.test.ts

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

describe('74062437', () => {

  test('should connect the websocket server', (done) => {
    const wss = new WebSocket.Server({ port: 0 }, () => {
      const socket = new Socket()
      socket.connect(`ws://localhost:${(wss.address() as WebSocket.AddressInfo).port}`);

      socket.socket.on('open', () => {
        expect(socket.socket.readyState).toEqual(WebSocket.OPEN);
        socket.socket.close();
      });

      socket.socket.on('close', () => wss.close(done));
    });
  });

  test('should throw error if already connected websocket server', (done) => {
    expect.assertions(2);
    const wss = new WebSocket.Server({ port: 0 }, () => {
      const socket = new Socket();
      socket.connect(`ws://localhost:${(wss.address() as WebSocket.AddressInfo).port}`);

      socket.socket.on('open', () => {
        expect(socket.socket.readyState).toEqual(WebSocket.OPEN);
        expect(() => socket.connect(`ws://localhost:${(wss.address() as WebSocket.AddressInfo).port}`)).toThrowError('Socket is already connected')
        socket.socket.close();
      });

      socket.socket.on('close', () => wss.close(done));
    });
  });
});

检测结果:

PASS  stackoverflow/74062437/socket.test.ts (10.964 s)
  74062437
    ✓ should connect the websocket server (31 ms)
    ✓ should throw error if already connected websocket server (6 ms)

-----------|---------|----------|---------|---------|-------------------
File       | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-----------|---------|----------|---------|---------|-------------------
All files  |     100 |      100 |     100 |     100 |                   
 socket.ts |     100 |      100 |     100 |     100 |                   
-----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        11.784 s

软件包版本:

"ws": "^8.9.0",
"jest": "^26.6.3"

相关问题