2015-11-13 39 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,如果您不能更改代码,请使用它。