node.jsexpresschaisinonava

Unable to stub method after module is loaded with sinon using ava and proxyquire


I am trying to stub a method doSendOtp defined in OtpService but stubbing not working.

It works with mocha, but with ava I don't know why it isn't working.

I am doing it using .serial but it doesnt help either.

/* eslint-disable no-unused-expressions */
const test = require('ava');
const request = require('supertest');
const sinon = require('sinon');
const { expect } = require('chai');

const app = require('../src/app');
const OtpService = require('../src/components/auth/otp.service');

test.before(async (t) => {
  t.context.server = request(app);
  t.context.baseUrl = '/api/v1/auth';
});

test.after.always((t) => {
  delete require.cache[require.resolve('../src/app')]; // kills server
});

test.serial('Send OTP', async (t) => {
  const { server, baseUrl } = t.context;
  const sendOtpStub = sinon.stub(OtpService, 'doSendOtp');
  const response = await server.get(`${baseUrl}/send-otp?phone=7845489898`)
    .expect(200);
  expect(sendOtpStub.calledOnce).to.be.true;
  expect(response).to.be.a('Object');
});

Test Failure Error.

Send OTP

  tests\auth.test.js:25

   24:     .expect(200);
   25:   expect(sendOtpStub.calledOnce).to.be.true;
   26:   expect(response).to.be.a('Object');

  Rejected promise returned by test. Reason:

  AssertionError {
    actual: false,
    expected: true,
    message: 'expected false to be true',
    showDiff: true,
  }

  » tests/auth.test.js:25:39

  ─

  1 test failed
npm ERR! Test failed.  See above for more details.

Update: I also tried using proxyquire but still not able to stub doSendOtp of OtpService.

/* eslint-disable no-unused-expressions */
const test = require('ava');
const request = require('supertest');
const sinon = require('sinon');
const { expect } = require('chai');
const proxyquire = require('proxyquire');
const app = require('../src/app');

test.before(async (t) => {
  const sendOtpStub = sinon.stub().resolves({});
  proxyquire('../src/components/auth/auth.module', {
    './otp.service': {
      doSendOtp: sendOtpStub,
    },
  });
  t.context.stubs = {
    sendOtpStub,
  };
  t.context.baseUrl = '/api/v1/auth';
  t.context.server = request(app);
});

test.after.always((t) => {
  delete require.cache[require.resolve('../src/app')]; // kills
});

test.serial('Send OTP', async (t) => {
  const { server, baseUrl, stubs } = t.context;
  const response = await server.get(`${baseUrl}/send-otp?phone=8576863491`)
    .expect(200);
  expect(stubs.sendOtpStub.calledOnce).to.be.true;
  expect(response).to.be.a('Object');
});

// test.serial('Login', async (t) => {

// })

Solution

  • I fixed it, I was stubbing after the module was loaded. I need to stub before the module is loaded.

    /* eslint-disable no-unused-expressions */
    const test = require('ava');
    const request = require('supertest');
    const sinon = require('sinon');
    require('dotenv').config();
    
    // dependency to be stubbed
    const OtpService = require('../src/components/auth/otp.service');
    
    // stubs
    const sendOtpStub = sinon.stub(OtpService, 'doSendOtp').resolves({});
    
    const app = require('../src/app');
    
    test.before(async (t) => {
      t.context.stubs = {
        sendOtpStub,
      };
      t.context.baseUrl = '/api/v1/auth';
      t.context.server = request(app);
    });
    
    test.after.always((t) => {
      delete require.cache[require.resolve('../src/app')]; // kills server
    });
    
    test.serial('Send OTP', async (t) => {
      const { server, baseUrl, stubs } = t.context;
      const res = await server.get(`${baseUrl}/send-otp?phone=77979879878`);
      t.is(res.status, 200);
      t.true(stubs.sendOtpStub.calledOnce);
      t.true(typeof res === 'object');
    });