最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Original function is called instead of stub - Stack Overflow

programmeradmin1浏览0评论

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 on retro.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
Add a ment  | 

2 Answers 2

Reset to default 9

You'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)

发布评论

评论列表(0)

  1. 暂无评论