Home > front end >  How to use ProxyRequire to mock NodeJs test
How to use ProxyRequire to mock NodeJs test

Time:09-17

I am writing unit test cases for the following file

//filename: a.js

var amqp = require("amqplib");

class RMQ {
  constructor(connectionURI) {
    this.URI = connectionURI;
  }
  async getInstance() {
    var connection = await amqp.connect(this.URI);
    this.connection = connection;
    return connection;
  }
}

const RMQ_INSTANCE = new RMQ(process.env.RMQ_URL);

module.exports = {
  RMQ_INSTANCE,
};

I am using the instance RMQ_INSTANCE in below file

// filename: b.js

const { RMQ_INSTANCE } = require("./a");

module.exports.publishEmail = async function(message) {
  var connection = await RMQ_INSTANCE.getInstance();
  var channel = await connection.createChannel();
  var exchange = "some_exchange";
  var key = "some_key";
  var msg = JSON.stringify(message);
  await channel.assertExchange(exchange, "topic", { durable: false });
  await channel.publish(exchange, key, Buffer.from(msg));
  setTimeout(function () {
    connection.close();
  }, 500);
}

I am using proxyrequire to mock the RMQ_INSTANCE in b.js

// filename: b.test.js

var proxyrequire = require("proxyquire").noCallThru();
var sinon = require("sinon");
const { assert } = require("sinon");

class fakeRMQClass {
  constructor(connectionURI) {
    this.URI = connectionURI;
  }
  async getInstance() {
    var connection = getFakeRMQStub()
    return connection;
  }
}

var producerTest = function () {
  it("producer connection - success test", async function () {
    var fakeRMQInstance = new fakeRMQClass("fake_url");
    var rmqUtils = proxyrequire("../path/to/b.js", {
      "./a": fakeRMQInstance
    });
    await rmqUtils.publishEmail("fake_msg");
  });
  afterEach(function () {
    sinon.verifyAndRestore();
  });
};
describe("test_producer", producerTest);

But I see the mocking is not working correctly. Could anyone please help me to mock this correctly?

CodePudding user response:

Since the RMQ_INSTANCE is an object, you can stub its methods using sinon.stub(obj, 'method'), you don't need to use proxyquire package.

Since you want to test the b module, the b module only cares about the interface of the RMQ_INSTANCE it depends on, and the specific implementation does not matter.

b.js:

const { RMQ_INSTANCE } = require('./a');

module.exports.publishEmail = async function (message) {
  var connection = await RMQ_INSTANCE.getInstance();
  var channel = await connection.createChannel();
  var exchange = 'some_exchange';
  var key = 'some_key';
  var msg = JSON.stringify(message);
  await channel.assertExchange(exchange, 'topic', { durable: false });
  await channel.publish(exchange, key, Buffer.from(msg));
  setTimeout(function () {
    connection.close();
  }, 500);
};

a.js:

class RMQ {
  constructor(connectionURI) {
    this.URI = connectionURI;
  }
  async getInstance() {}
}

const RMQ_INSTANCE = new RMQ(process.env.RMQ_URL);

module.exports = { RMQ_INSTANCE };

b.test.js:

const sinon = require('sinon');
const { RMQ_INSTANCE } = require('./a');
const { publishEmail } = require('./b');

describe('test_producer', () => {
  let clock;
  before(() => {
    clock = sinon.useFakeTimers();
  });
  after(() => {
    clock.restore();
  });
  it('producer connection - success test', async () => {
    const channelStub = {
      assertExchange: sinon.stub().returnsThis(),
      publish: sinon.stub(),
    };
    const connectionStub = { createChannel: sinon.stub().resolves(channelStub), close: sinon.stub() };
    sinon.stub(RMQ_INSTANCE, 'getInstance').resolves(connectionStub);
    await publishEmail('fake message');
    sinon.assert.calledOnce(RMQ_INSTANCE.getInstance);
    sinon.assert.calledOnce(connectionStub.createChannel);
    sinon.assert.calledWithExactly(channelStub.assertExchange, 'some_exchange', 'topic', { durable: false });
    sinon.assert.calledWithExactly(
      channelStub.publish,
      'some_exchange',
      'some_key',
      Buffer.from(JSON.stringify('fake message')),
    );
    clock.tick(500);
    sinon.assert.calledOnce(connectionStub.close);
  });
});

test result:

  test_producer
    ✓ producer connection - success test


  1 passing (10ms)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |     100 |      100 |      75 |     100 |                   
 a.js     |     100 |      100 |      50 |     100 |                   
 b.js     |     100 |      100 |     100 |     100 |                   
----------|---------|----------|---------|---------|-------------------
  • Related