I'm having an issue getting Sinon's stub to work correctly for me. When I stub list
on retro
and the test runs, app.get('/retro', retro.list)
is executing the original function retro.list
instead of the stub. Since this happens, the test fails because the stub's callCount
is 0.
I'm more familiar with coffeescript and I have stubbed things in the same way. Is there something I'm not understanding about Javascript's scoping, or how the require('../routes/retro')
works, or is retro
is not the same in app.js
and test.js
.
Much thanks for the help and code below.
test.js:
var request = require('supertest')
, retro = require('../routes/retro')
, app = require('../app')
, sinon = require('sinon');
require('should');
describe('GET /retro', function() {
// less involved, but maybe stupid to test
it('should call retro.list', function(done) {
var stub = sinon.stub(retro, 'list');
request(app)
.get('/retro')
.end(function(err, res){
stub.callCount.should.equal(1);
if (err) return done(err);
done();
})
})
})
app.js:
var express = require('express')
, config = require('./config')
, routes = require('./routes')
, retro = require('./routes/retro');
var app = express();
config(app);
app.get('/', routes.index);
app.get('/retro', retro.list);
module.exports = app;
retro.js:
var retro = {
list: function(req, res){
console.log('actual called');
res.send("respond with a resource");
}
}
module.exports = retro;
I'm having an issue getting Sinon's stub to work correctly for me. When I stub list
on retro
and the test runs, app.get('/retro', retro.list)
is executing the original function retro.list
instead of the stub. Since this happens, the test fails because the stub's callCount
is 0.
I'm more familiar with coffeescript and I have stubbed things in the same way. Is there something I'm not understanding about Javascript's scoping, or how the require('../routes/retro')
works, or is retro
is not the same in app.js
and test.js
.
Much thanks for the help and code below.
test.js:
var request = require('supertest')
, retro = require('../routes/retro')
, app = require('../app')
, sinon = require('sinon');
require('should');
describe('GET /retro', function() {
// less involved, but maybe stupid to test
it('should call retro.list', function(done) {
var stub = sinon.stub(retro, 'list');
request(app)
.get('/retro')
.end(function(err, res){
stub.callCount.should.equal(1);
if (err) return done(err);
done();
})
})
})
app.js:
var express = require('express')
, config = require('./config')
, routes = require('./routes')
, retro = require('./routes/retro');
var app = express();
config(app);
app.get('/', routes.index);
app.get('/retro', retro.list);
module.exports = app;
retro.js:
var retro = {
list: function(req, res){
console.log('actual called');
res.send("respond with a resource");
}
}
module.exports = retro;
Share
Improve this question
edited Aug 2, 2013 at 4:18
kmanzana
asked Aug 1, 2013 at 22:53
kmanzanakmanzana
1,1981 gold badge13 silver badges24 bronze badges
4
-
What does
retro
return? Is it a plain object or a newly created instance? – mor Commented Aug 1, 2013 at 23:00 -
1
Try this
retro.list.callCount.should.equal(1);
– Sushanth -- Commented Aug 1, 2013 at 23:05 -
@mor I included
retro.js
so you can see the object it returns. It is not an instance. I was thinking of exporting an instance and trying to stub the protoype, but that wasn't working very well either. – kmanzana Commented Aug 2, 2013 at 4:20 -
@Sushanth-- I tried out your advice, but
sinon.stub(retro, 'list')
returns the stub onretro.list
so they are the same object and running that code yielded the same problem. Thanks for the advice though – kmanzana Commented Aug 2, 2013 at 4:21
2 Answers
Reset to default 9You'll likely need to create your stubs before requiring/creating the app
.
var request = require('supertest')
, sinon = require('sinon')
, retro = require('../routes/retro');
var stubRetroList = sinon.stub(retro, 'list');
var app = require('../app');
// ...
stubRetroList.callCount.should.equal(1);
This allows retro.list
to be updated before it's passed to the route:
app.get('/retro', retro.list);
The issue is probably because retro.list
isn't being passed-by-reference (pointer), but is rather a reference that's passed-by-value (copied). So, though sinon.stub()
is altering retro.list
, it wouldn't affect the copy that the '/retro'
route already had.
I faced the same issue and the accepted answer (while true) was of no help. Turns out in order for sinon
stubbing to work the stubbed method cannot be used in the same module. In other words stubbing a module endpoint will only stub the module endpoint and not the internal usage of the function referenced by module.exports
.
Explained via an example:
module.js
const express = require('express')
const router = express.Router()
router.get('/', function (req, res) {
res.status(200).json(list())
})
function list() {
return ['something']
}
module.exports = {
router: router,
list: list
}
module.spec.js
// This stub will not work
sinon.stub(module, 'list').callsFake(() => ['something else'])
To make it work you have separate what you want to stub into its own module and use it that way:
sub_module.js
function list() {
return ['something']
}
module.exports = {
list: list
}
Now sub_module.list()
can be stubbed.
(OP defines a method in place so this is not an issue for him)