我怎样才能从一个开玩笑的类中模拟一个方法?

时间:2019-11-28 08:13:54

标签: node.js unit-testing jestjs

我正在使用node 12和Jest进行单元测试。我有一个代码可以通过websocket打开连接。

      const ws = require('ws');
      this.ws = new WebSocket(url);
      this.ws.on('open', () => {
        // How to test this callback?
        ...
        resolve();
      });
      this.ws.on('error', (err) => {
        // How to test this callback?
        ...
        reject(err)
      });

在我的测试案例中,我通过jtest模拟了ws模块:

const WebSocket = require('ws');

jest.mock('ws')

test('quote server listener should be able to connect to quote server', () => {
  const server = new QuoteServerListener(null, 'http://mock.com');
  server.connect();
  const mockWSInstance = WebSocket.mock.instances[0];
  expect(mockWSInstance.on).toHaveBeenCalledTimes(1);
});

上述测试用例可以正常工作。但是我不知道如何在ws.on('open', () => ...上触发对回调函数的调用。连接打开时,我想测试逻辑。我如何在模拟中实现这一目标? 我试图通过open之类的mockWSInstance实例发出一个mockWSInstance.emit('open', null)事件,但是它不会触发代码。在这种情况下我该怎么办?

1 个答案:

答案 0 :(得分:1)

这是单元测试解决方案: index.ts

export class SomeClass {
  ws;
  run() {
    const WebSocket = require("ws");
    const url = "";
    this.ws = new WebSocket(url);
    return new Promise((resolve, reject) => {
      this.ws.on("open", () => {
        resolve();
      });
      this.ws.on("error", err => {
        reject(err);
      });
    });
  }
}

index.spec.ts

import { SomeClass } from "./";
const WebSocket = require("ws");

jest.mock("ws", () => {
  const mWebSocket = {
    on: jest.fn()
  };
  return jest.fn(() => mWebSocket);
});

describe("SomeClass", () => {
  let instance;
  let ws;
  beforeEach(() => {
    ws = new WebSocket();
    instance = new SomeClass();
  });
  afterAll(() => {
    jest.resetAllMocks();
  });
  it("should pass", async () => {
    const eventHandler = {};
    ws.on.mockImplementation((event, handler) => {
      eventHandler[event] = handler;
    });
    const pending = instance.run();
    eventHandler["open"]();
    const actual = await pending;
    expect(actual).toBeUndefined();
    expect(ws.on).toBeCalledWith("open", eventHandler["open"]);
  });

  it("should fail", async () => {
    const eventHandler = {};
    ws.on.mockImplementation((event, handler) => {
      eventHandler[event] = handler;
    });
    const pending = instance.run();
    const mError = new Error("connection error");
    eventHandler["error"](mError);
    await expect(pending).rejects.toThrowError(mError);
    expect(ws.on).toBeCalledWith("error", eventHandler["error"]);
  });
});

覆盖率100%的单元测试结果:

 PASS  src/stackoverflow/59084313/index.spec.ts
  SomeClass
    ✓ should pass (4ms)
    ✓ should fail (3ms)

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

源代码:https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59084313