Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stub/Spies direct onto an Object #87

Closed
quangv opened this issue Feb 28, 2012 · 6 comments
Closed

Stub/Spies direct onto an Object #87

quangv opened this issue Feb 28, 2012 · 6 comments

Comments

@quangv
Copy link

quangv commented Feb 28, 2012

Hi, I'm new to BDD/TDD, Sinon.js is great btw :)

I'm using Sinon.js with Mocha & Node.js, and I was wondering if there was a way to add spies/stubs onto existing objects... so that any reference to that object now includes those spies... for example...

module.js

module.exports = function(){
}

subject.js

x = require('./module.js');
x()

test_subject.js

describe('subject', function(){
  it('should call x()', function(){
    x = require('./module.js');
    sinon.spy(x);
    require('./subject.js');
    assert.equal(x.called, true);  // failed
  });
})

might not be possible, thanks :)

just wanted to note that if module.js returns an object, it'll be spyable...

// module.js
exports.x = function(){
}

// subject.js
module = require('./module.js');
module.x();

// test_subject.js
module = require('./module.js');
sinon.spy(module, 'x');
require('./subject.js');
assert.equal(module.x.called, true);  // passes

related question for me, if X was instead a class, would it be possible? For example

// module.js
module.exports = function(){
}

// subject.js
X = require('./module');
new X;

// test_subject.js
X = require('./module');
sinon.spy(X, 'constructor');  // or sinon.spy(X.prototype, 'constructor'); ??
require('./subject');
assert.equal(X.calledWithNew(), true);

My understanding of how instances are created might be completely wrong. But is it correct that there is no way to turn an object into a spy, but instead have to create a new object, that's a spy? For example:

x = function(){};
sinon.spy(x);  // x isn't spied
y = sinon.spy(x)  // y is spied

x = {y:function(){}}
sinon.spy(x, 'y');  // x.y is spied, works; can the above work the same way?

Thank you :)

@neonstalwart
Copy link
Contributor

your problem is related to how your subject.js module gets a reference to the exports of module.js.

subject.js gets a direct reference to the exports of module.js rather than a reference to the spy that you create based on the exports of that module.

when you export an object, subject.js gets a reference to that object but when it tries to access a property of that object, you've already replaced the original function attached to that property with the spy function - this is why it works when you export an object.

your problem is not so much related to what you can or cannot spy on but rather it is a problem of what your subject.js gets a reference to when it loads. there's probably a clearer explanation but maybe that helps you see what's happening.

@cjohansen
Copy link
Contributor

As far as I can tell, there is no way to implement stubbing of functions that are directly exported (e.g module.exports = function () { ... }), because, like @neonstalwart said, you can replace the reference itself.

@funston
Copy link

funston commented Jun 26, 2012

why can't exporting a function reference work? it's perfectly valid JS and works in node.

this is valid:

var myObject = function () {};
module.exports = myObject;

@neonstalwart
Copy link
Contributor

@rschiavi the issue is not whether or not it's valid. the problem is that there's no way to get hold of the exports (or module) from outside of the module, so you can't replace the exported function with a stubbed version.

@kris-at-tout
Copy link

What about when you define your export like this:

module.js
module.exports = {
x: function() { }
}

subject.js
y = require('./module.js');
y.x();

test_subject.js
describe('subject', function() {
it('should call x()', function() {
var x = require('./module.js');
sinon.spy(x)
require('./subject.js');
assert.equal(x.called, true);
})
})

module is exporting an object, not a function directly. The error message I get is:

) subject should call x():
TypeError: Attempted to wrap undefined property undefined as function
at Object.wrapMethod (/Users/kris/node_modules/sinon/lib/sinon.js:65:23)
at Object.spy (/Users/kris/node_modules/sinon/lib/sinon/spy.js:42:22)
at Context. (/Users/kris/Development/ntest/test_subject.js:6:11)
at Test.Runnable.run (/usr/local/lib/node_modules/mocha/lib/runnable.js:184:32)
at Runner.runTest (/usr/local/lib/node_modules/mocha/lib/runner.js:300:10)
at Runner.runTests.next (/usr/local/lib/node_modules/mocha/lib/runner.js:346:12)
at next (/usr/local/lib/node_modules/mocha/lib/runner.js:228:14)
at Runner.hooks (/usr/local/lib/node_modules/mocha/lib/runner.js:237:7)
at next (/usr/local/lib/node_modules/mocha/lib/runner.js:185:23)
at Runner.hook (/usr/local/lib/node_modules/mocha/lib/runner.js:205:5)
at process.startup.processNextTick.process._tickCallback (node.js:244:9)

@cjohansen
Copy link
Contributor

@kris-at-tout You want to do:

sinon.spy(x, "x");

And:

x.x.called

That is, you stub the x property of the exported module from module.js

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants