Jest Mock Websocket Events - Typescript (binance-api-node)

20 views Asked by At

I'm using Jest to perform unit tests on my classes that handle the data that comes through Binance's webscoket streams. I use binance-api-node. I would like to simulate the event that sends a candle to test data reception in my test class. In my class I have a method called config.

class Klines extends Observable {
...
  config() {
    // DOC: https://www.npmjs.com/package/binance-api-node#candles-1
    // Configures monitoring of Candles type Websocket events client.ws.candles
    client.ws.candles(this.symbol, this.interval, (candle) => {
      this.addCandle(candle);
    });
  }
...

}

I am unable to simulate the client.ws.candles event to test my class 100% using Jest. Would anyone know how to mock this event?

My test is working, but i cant test the line 61 this.addCandle(candle);.

Thanks.

My Kline.ts file:

import { CandleChartInterval } from "binance-api-node";
import { HistoricCandles, Kline, WSCandle } from "./Kline";
import client from "./Client";
import Observable from "./Observable";

export class Klines extends Observable {
  klines: Kline[] = new Array<Kline>();
  symbol: string;
  interval: CandleChartInterval;
  limit: number;

  /**
   * Constructor for creating a new instance of the class.
   *
   * @param {string} symbol - the symbol for the instance
   * @param {CandleChartInterval} interval - the interval for the instance
   * @param {number} limit - the limit for the instance (default is 500)
   */
  constructor(
    symbol: string,
    interval: CandleChartInterval,
    limit: number = 500
  ) {
    super();
    this.symbol = symbol;
    this.interval = interval;
    this.limit = limit;
  }
  /**
   * Retrieves historical klines and adds them to the klines array.
   *
   * @param {void}
   * @return {Promise<void>}
   */
  async getHistoricalKlines() {
    const candles = await client.candles({
      symbol: this.symbol,
      interval: this.interval,
      limit: this.limit,
    });
    candles.forEach((candle) => this.addKline(new Kline(candle)));
  }
  /**
   * Method to start the process asynchronously.
   *
   * @return {Promise<void>} A promise that resolves when the process has started.
   */
  async start() {
    await this.getHistoricalKlines();
    this.config();
  }
  /**
   * Configures the client's WebSocket connection to receive candles for a specific symbol and interval.
   *
   * @param {type} symbol - the symbol for which to receive candles
   * @param {type} interval - the interval at which to receive candles
   * @return {type} - no return value
   */
  config() {
    client.ws.candles(this.symbol, this.interval, (candle) => {
      this.addCandle(candle);
    });
  }

  addCandle(candle: WSCandle) {
    if (candle.isFinal) {
      this.addKline(new Kline(candle));
    } else {
      this.klines.pop();
      this.addKline(new Kline(candle));
    }
  }

  addKline(kline: Kline) {
    this.klines.push(kline);
    if (this.klines.length > this.limit) {
      this.klines.shift();
    }
  }

  /**
   * Get the open times from the klines.
   *
   * @return {Date[]} array of open times
   */
  getOpenTimes(): Date[] {
    return this.klines.map((kline) => {
      return kline.openTime;
    });
  }
  /**
   * Get an array of opening prices from the klines.
   *
   * @return {number[]} array of opening prices
   */
  getOpens(): number[] {
    return this.klines.map((kline) => {
      return kline.open;
    });
  }
  /**
   * Retrieves an array of high values from the klines.
   *
   * @return {number[]} array of high values
   */
  getHighs(): number[] {
    return this.klines.map((kline) => {
      return kline.high;
    });
  }
  /**
   * Get an array of low values from the klines.
   *
   * @return {number[]} array of low values
   */
  getLows(): number[] {
    return this.klines.map((kline) => {
      return kline.low;
    });
  }
  /**
   * Get an array of close prices from the klines.
   *
   * @return {number[]} array of close prices
   */
  getCloses(): number[] {
    return this.klines.map((kline) => {
      return kline.close;
    });
  }
  /**
   * Retrieves an array of volumes from the klines.
   *
   * @return {number[]} array of volumes
   */
  getVolumes(): number[] {
    return this.klines.map((kline) => {
      return kline.volume;
    });
  }
  /**
   * Get an array of close times.
   *
   * @return {Date[]} array of close times
   */
  getCloseTimes(): Date[] {
    return this.klines.map((kline) => {
      return kline.closeTime;
    });
  }
}

My teste file:

import { mocked } from "jest-mock";
jest.mock("../src/lib/Client");
import client from "../src/lib/Client";
import candles from "./Candles.data";
import { HistoricCandles, WSCandle } from "../src/lib/Kline";
import { Klines } from "../src/lib/Klines";
import { CandleChartInterval } from "binance-api-node";
import wscandle from "./WSCancle.data";
const mockedClient = mocked(client, { shallow: true });
const data: HistoricCandles = candles;
const wsdata: WSCandle = wscandle;

describe("Klines", () => {
  afterEach(() => {
    jest.restoreAllMocks();
  });
  beforeEach(() => {
    mockedClient.candles.mockResolvedValue(data);
  });
  test("Should return a Klines", async () => {
    const klines = new Klines("SOLUSDT", CandleChartInterval.ONE_MINUTE, 5);
    expect(klines).toBeDefined();
    expect(klines.symbol).toBe("SOLUSDT");
    await klines.start();
    expect(klines.getCloseTimes().length).toBe(5);
  });
  test("Should add a candle event kline", async () => {
    const klines = new Klines("SOLUSDT", CandleChartInterval.ONE_MINUTE, 11);
    await klines.start();
    expect(klines.getCloseTimes().length).toBe(10);
    klines.addCandle(wsdata);
    expect(klines.getCloseTimes().length).toBe(11);
  });

  test("Should not add a candle event if kline is not final", async () => {
    const klines = new Klines("SOLUSDT", CandleChartInterval.ONE_MINUTE, 11);
    await klines.start();
    expect(klines.getCloseTimes().length).toBe(10);
    wsdata.isFinal = false;
    klines.addCandle(wsdata);
    expect(klines.getCloseTimes().length).toBe(10);
  });

  test("Should have 10 elements in each property", async () => {
    const klines = new Klines("SOLUSDT", CandleChartInterval.ONE_MINUTE, 11);
    await klines.start();
    wsdata.isFinal = true;
    klines.addCandle(wsdata);
    expect(klines.getCloseTimes().length).toBe(11);
    expect(klines.getCloses().length).toBe(11);
    expect(klines.getHighs().length).toBe(11);
    expect(klines.getLows().length).toBe(11);
    expect(klines.getOpenTimes().length).toBe(11);
    expect(klines.getOpens().length).toBe(11);
    expect(klines.getVolumes().length).toBe(11);
  });
});

I see this code to emulate the event client.ws.user, but i cant replicate it to client.ws.candles. I dont understand how it works.

jest.mock("../src/lib/Client", () => {
  return {
    ws: {
      user: jest.fn(),
    },
    // accountInfoData: jest.fn().mockResolvedValue(accountInfoData);
    accountInfo: jest.fn().mockResolvedValue({
      balances: [
        { asset: "BTC", free: "0.5", locked: "0.1" },
        { asset: "ETH", free: "1.5", locked: "0.3" },
        { asset: "SOL", free: "0", locked: "0.3" },
      ],
    }),
  };
});

\\ inside the test, this code trigger the event client.ws.user. But i dont know how it works!
    const mockBalancesEvent: { eventType: string; balances: AssetBalance[] } =
      outboundAccountPosition;
    // Simulate the event by calling the callback function passed to client.ws.user
    const userEventCallback = (client.ws.user as jest.Mock).mock.calls[0][0];
    await userEventCallback(mockBalancesEvent);
0

There are 0 answers