Mankind1023
Mankind1023

Reputation: 7732

Sinon spy calledWithNew not working

I'm trying to use sinon or sinon-chai's calledWithNew (or simply called), but can't seem to get it to work, I've looked at a few suggestions online without luck either, here is the function I'm trying to test:

users.js

exports.create = function (data) {
    //some validation

    var user = new User(data);

    return user.save().then((result) => {
        return mailer.sendWelcomeEmail(data.email, data.name).then(() => {
            return {
                message: 'User created',
                userId: result.id
            };
        });
    }).catch((err) => {
        return Promise.reject(err);
    });
}

Here is my test:

users.test.js

beforeEach(() => {
    saveStub = sandbox.stub(User.prototype, 'save').resolves(sampleUser);
    spy = sandbox.spy(User);

});

afterEach(() => {
    sandbox.restore();
});


it('should call user.save', async () => {
    result = await users.create(sampleArgs);


    expect(saveStub).to.have.been.called; //-> true
    expect(spy).to.have.been.called; //-> false, calledWithNew returns same result as well
});

I found several posts suggesting spying on (window, 'className') but I'm using mocha, not a browser.

Trying to spy on (global, User / User.prototype) didn't work either.

Upvotes: 0

Views: 873

Answers (1)

sripberger
sripberger

Reputation: 1732

User is a module-level variable in users.js. Sinon cannot affect it. When you do this in your test file:

spy = sandbox.spy(User);

You're creating a spy in the scope of your test file, sure, but your module is still using the original.

To do something like this, you need to export your constructor inside an object, then both invoke it and spy on it through that object:

Wherever User is coming from, let's say user.js:

class User {
    // whatever your User implementation is
}

module.exports = { User };

users.js:

const userModule = require('./user');

...

var user = new userModule.User(data);

Then, in your test file:

const userModule = require('./user');

spy = sandbox.spy(userModule, 'User');

Another way to do this would be to use something like proxyquire. It can make these kinds of tests less obtrusive, but can make your tests more confusing to readers.

My preference is generally to keep constructors very simple so I don't ever have to spy on them. I've never used calledWithNew in any of my own projects for this reason. :\ It's up to you, though.

Upvotes: 1

Related Questions