如何使用sinon模拟作为函数导出的节点模块的函数/属性?

时间:2016-07-13 15:29:29

标签: node.js testing sinon stub

我有一个作为函数导出的服务模块。我需要将一些东西传递给它,就像配置对象一样,所以它确实需要保留这个结构。我试图从服务中删除一个函数,但无法弄清楚。在我的应用程序中,我有一个函数可以在测试期间进行API调用,因此我想将其存根。 (我知道我必须以不同方式编写测试以处理异步问题)

// myService.js
module.exports = function(config) {
  function foo() {
    returns 'bar';
  }

  return {
    foo: foo
  };
};

// test.js
var config = require('../../config');
var request = require('supertest');
var chai = require('chai');
var expect = chai.expect;
var sinon = require('sinon');
var myService = require('./myService.js')(config);

describe('Simple test', function(done) {
  it('should expect "something else", function(done) {
    var stub = sinon.stub(myService, 'foo').returns('something else');

    request(server) // this object is passed into my test. I'm using Express
      .get('/testRoute')
      .expect(200)
      .expect(function(res) {
        expect(res.body).to.equal('something else');
        stub.restore();
      })
      .end(done);
  });
});

* /testRoute I set up as a simple GET route that simply returns the value from myService.foo()

以上不起作用,我认为这与我的服务导出方式有关。如果我按如下所示编写服务,则存根工作正常。

module.exports = {
  test: function() {
    return 'something';
  }
};

但同样,我需要能够将信息传递给模块,因此我希望将模块保留在上面的原始结构中。有没有办法从以这种方式导出的模块中存根函数?我也在寻找代理商,但不确定这是不是答案。

1 个答案:

答案 0 :(得分:3)

测试存根不起作用的原因是每次调用模块初始值设定项时都会创建foo函数。正如您所发现的,当您在模块上使用静态方法时,您就可以存根。

这个问题有多种解决方案 - 但最简单的方法是静态公开该方法。

// myService.js
module.exports = function(config) {
  return {
    foo: foo
  };
};

var foo = module.exports.foo = function foo() {
  return 'bar'
}

它很难看,但很有效。

如果foo函数对服务中的变量有一个闭包(这就是它存在于服务初始化程序中的原因),该怎么办?然后不幸的是,这些需要明确传入。

// myService.js
module.exports = function(config) {
  return {
    foo: foo
  };
};

var foo = module.exports.foo = function(config) {
  return function foo() {
    return config.bar;
  }
}

现在你可以安全地存根模块了。

但是,你如何存根应该被认为是不安全的。只有当您的测试完美无缺时,存根才会被清理干净。您应始终在beforeafter(或beforeEachafterEach)灯具中存根,例如:

// We are not configuring the module, so the call with config is not needed
var myService = require('./myService.js');

describe('Simple test', function(done) {
  beforeEach(function () {
    // First example, above 
    this.myStub = sinon.stub(myService, foo).returns('something else');
    // Second example, above
    this.myStub = sinon.stub(myService, foo).returns(function () {
      returns 'something else';
    });
  });

  afterEach(function () {
    this.myStub.restore();
  });

  it('should expect "something else", function(done) {
    request(server) // this object is passed into my test. I'm using Express
      .get('/testRoute')
      .expect(200)
      .expect(function(res) {
        expect(res.body).to.equal('something else');
       })
      .end(done);
   });
 });

还有其他选项可以使用依赖项注入来存储依赖项。我建议您查看https://github.com/vkarpov15/wagner-core或我自己的https://github.com/CaptEmulation/service-builder