【问题标题】:How can I mock a function/property of a node module exported as a function using sinon?如何使用 sinon 模拟导出为函数的节点模块的函数/属性?
【发布时间】:2016-07-13 15:29:29
【问题描述】:

我有一个作为函数导出的服务模块。我需要将一些东西传递给它,比如配置对象,所以它确实需要保留这个结构。我试图从服务中提取一个函数,但无法弄清楚。在我的应用程序中,我有一个函数会在测试期间进行有问题的 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';
  }
};

但是,我需要能够将信息传递给模块,所以我想将我的模块保留在上面的原始结构中。有没有办法从以这种方式导出的模块中存根函数?我也在研究 proxyquire 但不确定这是否是答案。

【问题讨论】:

    标签: node.js testing sinon stub


    【解决方案1】:

    您的测试存根不起作用的原因是每次调用模块初始化程序时都会创建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

    【讨论】:

    • 谢谢,这很有道理。我正在尝试深入研究 TDD 并建立更好的习惯,这肯定会有所帮助。我将按照您的建议静态公开这些功能。也感谢您对清理存根的提醒。我做了一个 afterEach,我正在跑去清理 :) 我想我应该把它包括在我的问题中!
    • 如果对你有用,请考虑接受我的回答
    • 刚刚做了。正在完成我之前的评论:p
    猜你喜欢
    • 2020-07-11
    • 2021-09-13
    • 2015-11-10
    • 2017-02-13
    • 1970-01-01
    • 2017-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多