Search code examples
javascriptnode.jssinonsinon-chai

stubbing class and methods using sinon stub


I need help in stubbing using sinon msk client functions connect() and send() or sendmessage(). Tried multiple ways but it still invokes the actual function. Please help

Here code snippets in the files.

index.js

var KafkaConnection = require('./mskclient');
                var kafkaObj = new KafkaConnection(key.User, key.Secret);
            var res = await kafkaObj.connectProducer(event.body);

mskclient.js

class KafkaConnection {
  constructor(user, secret) {
    this.connection = new Kafka({
      authenticationTimeout: 5000,
      clientId: client_id,
      brokers: brokerlist.split(','),
      ssl: true,
      sasl: {
        mechanism: 'scram-sha-512',
        username: user,
        password: secret,
      },
    });
    this.producer = null;
  }

  async sendMessage(msgObj) {
    console.log('sendMessage');
    this.producer = this.producer || this.connection.producer();
    await this.producer.connect();
    await this.producer.send(msgObj);
  }

  async disconnect() {
    this.producer && (await this.producer.disconnect());
  }

  async connectProducer(event) {
    try {
      log.info('Sending Message');
      console.log('connectProducer');

      await this.sendMessage({
        topic: process.env.TOPIC_NAME,
        acks: 1,
        messages: [
          {
            key: 'test-key',
            value: JSON.stringify(event),
          },
        ],
      });
      await this.disconnect();
      log.info('Successfully sent message');
      return 0;
    } catch (e) {
      log.info('Message not sent to kafka..');
      log.error(e);
      await this.disconnect();
      return 1;
    }
  }
}

module.exports = KafkaConnection;

so far I tried the following but both of them is getting called kafka actuall function and not stubs

approach #1

const service = require('../index');
const KafkaConnection = require('../mskclient');

var kafkaObj = sinon.createStubInstance(KafkaConnection);

var res1 = service.handler(event).then(function () {
                       done();
                }).catch((error) => {
                    done();
                })

approach #2

const service = require('../index');
const KafkaConnection = require('../mskclient');

//var kafkaObj = sinon.createStubInstance(KafkaConnection);
   SMSStub = sinon.stub(KafkaConnection.prototype, 'KafkaConnection').callsFake(() => {
                return 1
            });

var res1 = service.handler(event).then(function () {
                       done();
                }).catch((error) => {
                    done();
                })

Solution

  • First, service on its own import connection class independently.

    const service = require('../index'); // <-- import '../mskclient' and create obj
    const KafkaConnection = require('../mskclient');
    

    Even if you swap them

    const KafkaConnection = require('../mskclient');
    
    const kafkaObj = sinon.createStubInstance(KafkaConnection);
    
    const service = require('../index'); // <-- it is still importing the original mskclient
    

    From https://sinonjs.org/how-to/stub-dependency/, if you really want to stub the dependencies, you will have to export a mutable object:

    // mskclient.js
    module.exports = {
      KafkaConnection: class KafkaConnection {},
    };
    
    // service.js
    const mod = require('./mskclient');
    
    module.exports = function run() {
      const conn = new mod.KafkaConnection();
      conn.doSomething();
    }
    

    But if you use jest, you could

    jest.mock('../mskclient');
    // or
    jest.mock('../mskclient', () => jest.fn(() => ({
      connectProducer: jest.fn(),
    })));
    
    const service = require('../index'); // <-- import '../mskclient' and create obj
    const KafkaConnection = require('../mskclient');
    
    // es6
    import service from '../';
    import KafkaConnection from '../mskclient';
    
    jest.mock('../mskclient', () => jest.fn(() => ({
      connectProducer: jest.fn(),
    })));
    

    SEE: https://jestjs.io/docs/next/es6-class-mocks#automatic-mock