2015-11-13 33 views
3

其他節點開發人員如何在單元測試中使用sinon模擬構造函數調用?例如,假設我有一些函數foo在節點中模仿構造函數

function foo() { 
    var dependency = new Dependency(args); 
    // do stuff with dependency 
} 
exports.module.foo = foo; 

,並在一個單獨的測試文件我有一些測試中,我想覈實一下相關的構造函數被調用(參數),我需要控制什麼返回

​​

的問題是,興農可以附加到一個對象只有模擬功能,所以我們要構造附加到一個對象時,它可以被嘲笑了。

我一直在做的只是把對象的構造函數連接到製作構造函數調用的模塊中,調用構造函數作爲對象的方法,然後導出對象使用它在測試中:

var Dependency = require('path/to/dependency'); 

var namespace = { 
    Dependency: Dependency 
} 

function foo() { 
    var dependency = new namespace.Dependency(args); 
    // do stuff with dependency 
} 
exports.moduole.foo = foo; 
exports.module.namespace = namespace; 

testfile的:

it('should call Dependency constructor with bar', function() { 
    var foo = require('myModule').foo; 
    var namespace = require('myModule').namespace; 

    var DependencyMock = sinon.mock(namespace, 'Dependency').returns(0); 
    foo(); 
    expect(DependencyMock.calledWith(bar)).to.equal(true); 
}); 

這工作,但感覺很笨重,露出我的模塊上的對象只是爲了測試它的緣故。

任何提示?

回答

5

我認爲這是值得一問爲什麼你要嘲笑依賴而不是注入這種依賴性的構造?

考慮您的示例代碼:

// in "foo.js" 
function foo() { 
    var dependency = new Dependency(args); 
    // do stuff with dependency 
} 
exports.module.foo = foo; 

如果Dependency需要foo工作,你可以把它注射作爲foo參數:

// in "foo.js" 
function foo(dependency) { 
    // do stuff with dependency 
} 
exports.module.foo = foo; 

// in "bar.js" 
var foo = require('./foo.js')(new Dependency(args)); 

隨着這一變化是現在瑣碎注入任何在您的測試中測試雙倍(要了解有關JavaScript測試雙打的更多信息,請參閱我的article on the subject)。

這種方法使得你的函數/模塊的依賴性是明確的,但需要你在某些時候連接它們(這裏是:require('./foo.js')(new Dependency(args));)。


如果你不想連線東西手動有你可以採取使用rewirereplacing constructor with factory method另一種方法:

// in "dependency.js" 
module.exports= function(args) { 
    return new Dependency(args); 
} 

// in "foo.js" 
var dependency = require('./dependency); 

function foo() { 
    var dep = dependency(args); 
    // do stuff with dependency 
} 
exports.module.foo = foo; 

,並在您的測試:

var rewire = require("rewire"), 
    foo = rewire("../lib/foo.js"); 

it('should call dependency... ', function() { 
    foo.__set__("dependency", /* some spy */); 

    foo(); 
}); 

希望這幫助!

0

沒有嘗試過,但這可以工作:存根依賴的構造函數,並讓它返回一個模擬。

var constructor = sinon.stub(Dependency.prototype, "constructor"); 
constructor.returns(sinon.mock(Dependency)); 
+0

感謝您的回覆。據我所知,我遇到了同樣的問題,因爲我不能監視Dependency的實際構造函數調用,因爲Dependency不是任何屬性。問題在於我如何設計我的代碼,並且使用像上面Jan所示的依賴注入似乎對我來說是最好的解決方案。 –

0

我遇到了類似的問題,揭露功能,而非實物模塊 - this was my solution。只是把這裏作爲解決問題的另一種方式,但我必須說,通過更好的設計解決問題,就像Jan Molak所說,似乎是一種更好的方法。

2

我用於此解決方法:

// Keep a reference to your dependancy class. 
const dependencyClass = Dependency; 
let item = new Dependency(); 
// Replace class with a stub.(optionally return an item. 
Dependency = sinon.stub(Dependency, 'constructor').returns(item); 

// Call your code that you expect the class constructor to be called. 
foo(); 

assert.isTrue(Dependency.calledWithNew()); 
assert.equal(1, Dependency.callCount); 
// Restore class reference. 
Dependency = dependencyClass; 

另外,在上述情況下,被返回的項目,因此用戶可以訪問用於進一步測試的依賴性。 例如。

assert.equal(item.someValue, 10); 

你可以有其他問題使用它,例如定義的屬性將不再可用於類。 我同意Jan Molek,如果您不能更改代碼,請使用它。