将整个班级存入Sinon进行测试

fea*_*ool 12 javascript unit-testing node.js sinon

序言:我已经阅读了很多SO和博客文章,但没有看到任何回答这个特定问题的内容.也许我只是在寻找错误的东西......

假设我正在开发一个WidgetManager可以操作Widget对象的类.

如何使用sinon测试WidgetManager是否Widget正确使用API而不需要拉入整个Widget库?

基本原理:WidgetManager的测试应该与Widget类分离.也许我还没有编写Widget,或者Widget可能是一个外部库.无论哪种方式,我应该能够测试WidgetManager正确使用Widget的API而无需创建真实的Widgets.

我知道sinon模拟只能在现有的类上工作,据我所知,sinon存根也需要该类存在才能被存根.

为了使它具体化,我将如何测试Widget.create()在下面的代码中使用单个参数'name'调用一次?

正在测试的代码

// file: widget-manager.js

function WidgetManager() {
   this.widgets = []
}

WidgetManager.prototype.addWidget = function(name) {
    this.widgets.push(Widget.create(name));
}
Run Code Online (Sandbox Code Playgroud)

测试代码

// file: widget-manager-test.js

var WidgetManager = require('../lib/widget-manager.js')
var sinon = require('sinon');

describe('WidgetManager', function() {
  describe('#addWidget', function() {
    it('should call Widget.create with the correct name', function() {
      var widget_manager = new WidgetManager();
      // what goes here?
    });

    it('should push one widget onto the widgets list', function() {
      var widget_manager = new WidgetManager();
      // what setup goes here?
      widget_manager.addWidget('fred');
      expect(widget_manager.widgets.length).to.equal(1);
  });
});
Run Code Online (Sandbox Code Playgroud)

旁白:当然,我可以MockWidget使用适当的方法定义一个类进行测试,但我更感兴趣的是真正学习如何正确使用sinon的spy/stub/mock工具.

dun*_*all 10

答案实际上是依赖注入.

您希望测试以预期方式WidgetManager依赖项(Widget)进行交互- 并且您希望自由地操纵和询问该依赖项.为此,您需要Widget在测试时注入存根版本.

根据WidgetManager创建方式,有几种依赖注入选项.

一个简单的方法是允许将Widget依赖项注入WidgetManager构造函数:

// file: widget-manager.js

function WidgetManager(Widget) {
   this.Widget = Widget;
   this.widgets = [];
}

WidgetManager.prototype.addWidget = function(name) {
    this.widgets.push(this.Widget.create(name));
}
Run Code Online (Sandbox Code Playgroud)

然后在你的测试中,你只需将一个短线传递WidgetWidgetManager测试中:

it('should call Widget.create with the correct name', function() {
  var stubbedWidget = {
      create: sinon.stub()
  }
  var widget_manager = new WidgetManager(stubbedWidget);
  widget_manager.addWidget('fred');
  expect(stubbedWidget.create.calledOnce);
  expect(stubbedWidget.create.args[0] === 'fred');
});
Run Code Online (Sandbox Code Playgroud)

您可以根据特定测试的需要修改存根的行为.例如,要测试创建窗口小部件后窗口小部件列表长度是否递增,您只需从存根create()方法返回一个对象:

  var stubbedWidget = {
      create: sinon.stub().returns({})
  }
Run Code Online (Sandbox Code Playgroud)

这使您可以完全控制依赖关系,而无需模拟或存根所有方法,并允许您测试与其API的交互.

也有类似的选项proxyquire重新连接这给在测试时间重写依赖更强大的选择.最合适的选择是实现和首选 - 但在所有情况下,您只是想在测试时替换给定的依赖项.