无法使用 sinon 模拟 ioredis 连接
Unable to mock ioredis connection using sinon
我正在尝试使用 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 连接。我正在尝试存根 'connect' 事件,但在调试它时我发现该事件从未触发(甚至没有触发错误事件)。
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();
});
});
测试此服务的正确方法是什么?为什么用这种方式存根时没有触发事件?
谢谢
这是一个如何存根 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 毫秒的默认超时错误,因为 完成 未被调用。
我正在尝试使用 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 连接。我正在尝试存根 'connect' 事件,但在调试它时我发现该事件从未触发(甚至没有触发错误事件)。
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();
});
});
测试此服务的正确方法是什么?为什么用这种方式存根时没有触发事件?
谢谢
这是一个如何存根 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 毫秒的默认超时错误,因为 完成 未被调用。