Miki
Miki

Reputation: 1818

How to stub an object method with sinon?

I need to stub the sendMandrill method of the mh object.

See my file under test (mail.js):

let MailHandler = require('../../modules/mail.handler.module');
...
let api = (router, parser) => {
   let send = async (req, res, next) => {
      let mh = new MailHandler();
      mh.sendMandrill();    
      ...
   }
   ...    
   return router.post('/mail/send', parser.json(), send);
}
module.exports = api;
...

My test (mail.spec.js):

let stRequest = require('supertest');
let MailHandler = require('../../modules/mail.handler.module');
describe('my test', () => {
   beforeEach(() => {
      sinon.stub(MailHandler.prototype, 'sendMandrill', () => true);
   })
   it('stubs sendMandrill!', done => {
      stRequest(app)
         .post('/mail/send')
            .end((err, resp) => {
                done();
            });
   })
})

Currently I'me getting the error below:

TypeError: Cannot stub non-existent own property sendMandrill

Adding mail.handler.module - See below the mailHandler / sendMandrill code:

module.exports = mailHandler;

function mailHandler() {
    ...
    var mandrill = require('../modules/mandrill');

    var handler = {
        sendMandrill: sendMandrill,
        ...
    };

    return handler;

    function sendMandrill() {
        mandrill.messages.sendTemplate({
            message: {...}
        });
    }
    ...
}

Upvotes: 0

Views: 2563

Answers (1)

Yury Tarabanko
Yury Tarabanko

Reputation: 45121

You current approach creates a new sendMandrill for each and every instance created by mailHandler factory. You should actually call it w/o new let mh = mailHandler() or even better rename it to createMailHandler to avoid misuse.

If you want to effectively use prototype inheritance you'll need to rewrite mailHandler to use actually use this instead of a newly created object.

var mandrill = require('../modules/mandrill');

module.exports = MailHandler;

function MailHandler() {
    // use this instead of newly created object
    this.foo = 'bar'

    // avoid explicit return
    // return handler;
}

// set methods to prototype
MailHandler.prototype.sendMandrill = function sendMandrill() {
        // use this instead of handler here
        mandrill.messages.sendTemplate({
            message: {...}
        });
    }

Using the above approach you would be able to stub prototype properties via sinon and justify calling the constructor with new keyword.

UPD

If you have no control over mail.handler.module you could either use rewire module that allows to mock entire dependencies or expose MailHandler as a part of your api module to make it injectable.

api.MailHandler = require('../../modules/mail.handler.module')

let mh = api.MailHandler();

And then in tests

let oldMailHandler;

beforeAll(() => { oldMailHandler = api.MailHandler})
afterAll(() => { api.MailHandler = oldMailHandler})
beforeEach(() => { api.MailHandler = function MockMailHandler() {} })

Upvotes: 1

Related Questions