簡體   English   中英

將整個班級存入Sinon進行測試

[英]stubbing an entire class for testing in sinon

序言:我已經閱讀了很多SO和博客文章,但沒有看到任何回答這個特定問題的內容。 也許我只是在尋找錯誤的東西......

假設我正在開發一個將在Widget對象上運行的WidgetManager類。

如何使用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));
}

測試代碼

// 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);
  });
});

MockWidget :當然,我可以使用適當的方法定義一個MockWidget類進行測試,但我更感興趣的是真正學習如何正確使用sinon的spy / stub / mock工具。

答案實際上是依賴注入。

您希望測試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));
}

然后在測試中,您只需將一個存根的Widget傳遞給測試中的WidgetManager

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');
});

您可以根據特定測試的需要修改存根的行為。 例如,要測試創建窗口小部件后窗口小部件列表長度是否遞增,您只需從存根的create()方法返回一個對象:

  var stubbedWidget = {
      create: sinon.stub().returns({})
  }

這使您可以完全控制依賴關系,而無需模擬或存根所有方法,並允許您測試與其API的交互。

也有類似的選項proxyquire重新連接這給在測試時間重寫依賴更強大的選擇。 最合適的選擇是實現和首選 - 但在所有情況下,您只是想在測試時替換給定的依賴項。

你的addWidget方法做了兩件事:

  • “將”字符串“轉換”為Widget實例;
  • 將該實例添加到內部存儲。

我建議您更改addWidget簽名以直接接受實例,而不是名稱,並移出創建其他地方。 將使測試更容易:

Manager.prototype.addWidget = function (widget) {
  this.widgets.push(widget);
}

// no stubs needed for testing:
const manager = new Manager();
const widget = {};

manager.addWidget(widget);
assert.deepStrictEquals(manager.widgets, [widget]);

在那之后,你需要一種按名稱創建小部件的方法,這也應該是非常簡單的測試:

// Maybe this belongs to other place, not necessarily Manager class…
Manager.createWidget = function (name) {
  return new Widget(name);
}

assert(Manager.createWidget('calendar') instanceof Widget);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM