我无法对我想要在打字稿中测试的方法使用的方法进行存根处理。为了清晰起见,我在示例中删除了很多方法本身,但基本上我有getServiceWithRetry
方法调用getService
方法。
即
export function getServiceWithRetry(name:string, triesLeft:number) {
//do stuff
getService(name)
//do more stuff
}
export function getService(name:string) {
//lookup stuff
}
这是Lookup
导入我的测试中的。如果我在测试中调用getService
,我可以成功地删除getService方法,但是当我运行getServiceWithRetry
时,它会调用实际的getService
方法,而不是存根。有谁知道我做错了什么?
it("test", function(done) {
let serviceStub = sinon.stub(Lookup, 'getService')
serviceStub.returns(Promise.resolve("resolved"))
//this uses the stub
Lookup.getService("name").then(function(value) {
console.log("success: "+value)
}, function(error) {
console.log("error: "+error)
})
//this calls the actual method, not the stub as I would expect it to
Lookup.getServiceWithRetry("serviceName", 4).then(function(value) {
console.log("success: "+value)
}, function(error) {
console.log("error: "+error)
})
done()
})
注意:对于那些不熟悉蓝鸟承诺的人,.then(function(value){}, function(error){})
方法会处理如果承诺成功并且承诺被拒绝会发生什么。
答案 0 :(得分:3)
您需要更改:
export function getServiceWithRetry(name:string, triesLeft:number) {
//do stuff
getService(name)
//do more stuff
}
为:
export function getServiceWithRetry(name:string, triesLeft:number) {
//do stuff
this.getService(name)
//do more stuff
}
当您拨打Lookup.getServiceWithRetry()
getService()
来电时,Lookup.getService()
将会指向getService()
,而不是您所导出的模块中的apple dog orange
。
答案 1 :(得分:2)
问题在于,sinon.stub(Lookup, 'getService')
您正在改变您在测试中保留的Lookup变量的内部,然后从该变量中获取方法。在Lookup模块中,虽然该函数只是直接从其本地范围查找getService
。在外部,我不会认为你有任何方式可以搞砸这个范围,所以我不敢轻易修复这个问题。
通常,您通常无法在测试中模拟单个模块的某些部分。你需要重新调整一下,并且有几个选项:
完全单独测试它们。将getServiceWithRetry更改为通用retry
方法,例如所以你可以像retry(nTimes, getService, "serviceName")
或retry(() => getService("serviceName"), nTimes)
一样调用它。如果这样做是切实可行的(即如果它没有太多地将它绑定到getService
)那么你可以自己轻松地测试它:
var myStub = sinon.stub();
myStub.onCall(0).throw("fail once");
myStub.onCall(0).throw("fail twice");
myStub.returns(true); // then return happily
expect(retry(myStub, 1)).to.throw("fail twice"); // gives up after one retry
expect(retry(myStub, 5)).to.return(true); // keeps going to success
如果您想在其他地方调用单个getServiceWithRetry,您可以轻松地构建一个:var getServiceWithRetry = (arg, triesLeft) => retry(getService, tries)
放弃并一起测试它们。这意味着删除getService
所依赖的内容,而不是直接对其进行存根。这取决于您希望从测试中获得的粒度级别,但如果此代码很简单并且您可以更粗略地进行测试,那么这可能是一个简单的选择。
即使你单独分开它们,也可能想要这样做,以获得额外覆盖的单元和集成测试。如果它们之间发生了一些更复杂的相互作用,那就更加真实了。
从我所看到的情况来看,可能与这种情况无关,但在其他情况下,类似于将测试中的方法(getServiceWithRetry)放在类中,并使用依赖注入。您将创建一个类,该类在其构造函数中获取依赖项(getService方法),在内部存储它,并在以后在结果对象上调用方法时使用它。在你的生产代码中,其他东西必须正确地将它们粘合在一起,然后在你的测试中你可以传入一个存根。
我希望这种情况也有些过分,但您可以将getService
拉入Lookup导入的完全独立的模块中,并使用类似Rewire之类的内容将其交换为其他模块测试
这与依赖注入选项非常相似,并且使您的生产代码更简单,但代价是使您的测试代码更加复杂和神奇。
答案 2 :(得分:0)
由于您使用的是TypeScript,因此最好使用ts-mockito
(npm install --save ts-mockquito
)。
ts-mockito
支持类型。
然后你可以模拟你的类(来自自述文件,稍加修改):
// Creating mock
let mockedFoo:Foo = mock(Foo);
// Getting instance from mock
let foo:Foo = instance(mockedFoo);
// Using instance in source code
foo.getBar(3);
foo.getBar(5);
// Explicit, readable verification
verify(mockedFoo.getBar(3)).called();
verify(mockedFoo.getBar(5)).called();
when(mockedFoo.getBar(4)).thenReturn('three');