首页 > 解决方案 > 无法使用 sinon 模拟 ioredis 连接

问题描述

我正在尝试使用 Sinon 为以下服务创建单元测试。如您所见,在构造函数上调用了“_createRedisConnection”,因此在单元测试中我必须模拟 Redis 连接。

import { inject, injectable } from "inversify";
import { TYPES } from "../../inversify/types";
import { Logger } from "winston";
import { Config } from "../../interfaces/config.interface";
import { BaseService } from "../base.service";
import * as Redis from "ioredis";
import { HttpResponseError } from "../../interfaces/HttpResponseError.interface";
import { BaseResponse } from "../../interfaces/BaseResponse.interface";

@injectable()
export class RedisService extends BaseService {
  private _redisClient;
  private _isRedisConnected: boolean;

  constructor(@inject(TYPES.Logger) private logger: Logger,
    @inject(TYPES.Config) private config: Config) {
    super(logger, config);
    this._isRedisConnected = false;
    this._createRedisConnection();
  }

  public async set(key, value, epu, receivedTtl): Promise<BaseResponse> {
    if (this._isRedisConnected) {
      const encryptedKey = this.createEncryptedKey(epu, key);

      if (!encryptedKey || !value) {
        throw new HttpResponseError("General error", "Missing attributes in request body", 422);
      }

      const ttl = this.limitTtl(receivedTtl);

      let response;

      if (ttl >= 0) {
        await this._redisClient.setex(encryptedKey, ttl, value)
        .then(() => {
          response = new BaseResponse("success", "Data saved successfully", ttl);
        })
        .catch((errorMessage: string) => {
          throw new HttpResponseError("General error", `Error while saving data. err = ${errorMessage}`, 500);
        });
      } else {
        await this._redisClient.set(encryptedKey, value)
        .then(() => {
          response = new BaseResponse("success", "Data saved successfully", ttl);
        })
        .catch((errorMessage: string) => {
          throw new HttpResponseError("General error", `Error while saving data. err = ${errorMessage}`, 500);
        });
      }

      return response;
    }

    throw new HttpResponseError("General error", "Cache is not responding", 503);
  }

  private _createRedisConnection(): void {
    this._redisClient = new Redis({
      sentinels: [{ host: this.config.redisConfig.host, port: this.config.redisConfig.port }],
      name: "mymaster",
      dropBufferSupport: true,
    });

    this._redisClient.on("connect", () => {
       this._isRedisConnected = true;
      });

    this._redisClient.on("error", (errorMessage: string) => {
      this._isRedisConnected = false;
    });
  }
}

我的问题是模拟 Redis 连接。我正在尝试存根“连接”事件,但是在调试它时,我看到该事件从未触发(甚至不是错误事件)。

import "reflect-metadata";
import { expect } from "chai";
import { Logger } from "winston";
import * as Redis from "ioredis";
import { stub } from "sinon";
import { RedisService } from "./redis.service";
import { config } from "../../config";

class LoggerMock {
    public info(str: string) { }
    public error(str: string) { }
}

describe("RedisService Service", () => {
    const redisStub = stub(Redis.prototype, "connect").returns(Promise.resolve());
    const logger = new LoggerMock() as Logger;
    const redisService = new RedisService(logger, config);

    it("Should success set data", async () => {
        const redisClientStub = stub(Redis.prototype, "set").resolves(new Promise((resolve, reject) => { resolve('OK'); }));
        const result = await redisService.set("key", "value", "epu", -1);
        expect(result.message).to.equals("success");
        expect(result.response).to.equals("Data saved successfully");

        redisClientStub.restore();
        redisStub.restore();
    });
});

测试此服务的正确方法是什么?为什么以这种方式存根时没有触发事件?

谢谢

标签: node.jsunit-testingmocha.jssinonioredis

解决方案


这是一个如何对 ioredis Redis.prototype.connect进行存根的示例。

// File test.js
const { expect } = require('chai');
const Redis = require('ioredis');
const sinon = require('sinon');

describe('connection', function () {
  it('should emit "connect" when connected', function (done) {
    // Create stub on connect.
    const stubRedisConnect = sinon.stub(Redis.prototype, 'connect');
    stubRedisConnect.callsFake(async function () {
      // This will trigger connect event.
      this.setStatus('connect');
    });
    const redis = new Redis();
    redis.on('connect', function () {
      // Do not forget to restore the stub.
      stubRedisConnect.restore();
      done();
    });
  });
});

当我在终端上运行它时:

$ npx mocha test.js


  connection
    ✓ should emit "connect" when connected


  1 passing (6ms)

如果测试存根失败,将有 2000 毫秒的默认超时错误,因为没有被调用。


推荐阅读