nodejs 中的 Redis 模拟无法存根不存在 属性 createClient

Redis mock in nodejs cannot stub non-existent property createClient

我在尝试使用 mocha 和 sinon 模拟 NodeJS 中的 redis createClient() 方法时遇到问题。这是我 index.js 的片段。在socketclass里面,有create redis connection。现在在我的单元测试中,我遇到了这个错误TypeError: Cannot stub non-existent property createClient。我似乎无法弄清楚为什么?是因为模拟的某些顺序吗?

const express = require("express");
const http = require('http');
const redis = require('redis');
const expressApp = express();
const server = http.createServer(expressApp);
const io = require('socket.io')(server, {
    pingInterval: 10000,
    pingTimeout: 5000
});
const config = require('config');
const log = require('gelf-pro');
const HTTP_PORT = 3000;

// Socket IO call backs
io.on("connection", (client) => {    
    new Socket(client);
});

// export the server so it can be easily called for testing
exports.server = server.listen(HTTP_PORT, () => {
    log.info('socketio server started at port ' + HTTP_PORT);
});

单元测试代码:

'use strict'

var expect = require('chai').expect
  , redis = require('redis')
  , redisMock = require('redis-mock')
  , sinon = require('sinon')
  , io = require('socket.io-client')
  , ioOptions = { 
      transports: ['websocket']
    , forceNew: true
    , reconnection: false
  }
  , testMsg = JSON.stringify({message: 'HelloWorld'})
  , sender
  , receiver
  



describe('Chat Events', function(){
  beforeEach(function(done){

    sinon
      .stub(redis.RedisClient.prototype, 'createClient')
      .callsFake(function() {
        console.log('mock redis called');
        return redisMock.createClient();
      });
    
    // connect two io clients
    sender = io('http://localhost:3000/', ioOptions)
    receiver = io('http://localhost:3000/', ioOptions)
    
    // finish beforeEach setup
    done()
  })
  afterEach(function(done){
    
    // disconnect io clients after each test
    sender.disconnect()
    receiver.disconnect()
    done()
  })

  describe('Message Events', function(){
    it('Clients should receive a message when the `message` event is emited.', function(done){
      sender.emit('message', testMsg)
      receiver.on('ackmessage', function(msg){
        expect(msg).to.contains(testMsg)
        done()
      })
    })
  })
})

redis.createClient 方法是一个独立函数,不是 原型方法。如果您使用 TypeScript,我们可以检查 redis 包的类型。

index.d.ts:

export function createClient(port: number, host?: string, options?: ClientOpts): RedisClient;
export function createClient(unix_socket: string, options?: ClientOpts): RedisClient;
export function createClient(redis_url: string, options?: ClientOpts): RedisClient;
export function createClient(options?: ClientOpts): RedisClient;

所以,你应该像这样存根:

index.js:

const redis = require('redis');

function main() {
  redis.createClient();
}

module.exports = main;

index.test.js:

const main = require('./');
const redis = require('redis');
const sinon = require('sinon');

describe('62909606', () => {
  it('should pass', () => {
    const createClientStub = sinon.stub(redis, 'createClient').callsFake(function() {
      console.log('mock redis called');
    });
    main();
    sinon.assert.calledOnce(createClientStub);
  });
});

带有覆盖率报告的单元测试结果:

  62909606
mock redis called
    ✓ should pass


  1 passing (9ms)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |     100 |      100 |     100 |     100 |                   
 index.js |     100 |      100 |     100 |     100 |                   
----------|---------|----------|---------|---------|-------------------