Javascript 西农间谍从未打过电话,但应该打 问题

Javascript 西农间谍从未打过电话,但应该打 问题,javascript,unit-testing,sinon,spy,Javascript,Unit Testing,Sinon,Spy,我正在使用Jest和SinonJS测试一个定制的redux中间件,更确切地说,我想测试一些函数是否在中间件内部的特殊条件下被调用 我用SinonJS来制作间谍,我开玩笑地进行测试。我为我想要跟踪的特定功能初始化了spies,当我检查spies是否已被调用时,即使它应该被调用(手动测试),spies也没有被调用 代码 下面是我要测试的中间件: 从'react cookie'导入{cookie}; 从“/setAuthorizationToken”导入setAuthorizationToken; 让

我正在使用Jest和SinonJS测试一个定制的redux中间件,更确切地说,我想测试一些函数是否在中间件内部的特殊条件下被调用

我用SinonJS来制作间谍,我开玩笑地进行测试。我为我想要跟踪的特定功能初始化了spies,当我检查spies是否已被调用时,即使它应该被调用(手动测试),spies也没有被调用

代码 下面是我要测试的中间件:

从'react cookie'导入{cookie};
从“/setAuthorizationToken”导入setAuthorizationToken;
让cookies=新cookies();
export const bindTokenWithApp=(存储)=>(下一步)=>(操作)=>{
//在操作之前选择令牌
const-previousToken=getToken(store.getState());
//派遣行动
常量结果=下一步(操作);
//选择已调度操作后的令牌
const nextToken=getToken(store.getState());
如果(上一个令牌!==下一个令牌){
如果(nextToken==''){
setAuthorizationToken(false);
删除(会话\u COOKIE\u名称、COOKIE\u选项);
}否则{
cookies.set(会话\u COOKIE\u名称、nextToken、COOKIE\u选项);
setAuthorizationToken(nextToken);
}
}
返回结果;
};
这是我的实际测试

从“/中间件”导入{bindTokenWithApp};
从“react cookie”导入{cookie};
从'sinon'导入sinon,{assert};
从“/setAuthorizationToken”导入setAuthorizationToken;
描述('bindTokenWithApp',()=>{
const next=jest.fn();
const action=jest.fn();
让cookies=新cookies();
它('在没有令牌时删除Cookie',()=>{
//我的实际工作不是间谍
const cookieSpy=sinon.spy(cookies.remove);
const authSpy=sinon.spy(setAuthorizationToken);
//特定情况下的存根。此代码有效,
//我登录到中间件,得到以下值
const getState=sinon.stub();
返回({auth:{token:'a token'}});
getState.onSecondCall()返回({auth:{token:'''}});
常量存储={getState:getState};
bindTokenWithApp(商店)(下一步)(行动);
assert.calledOnce(cookieSpy);
assert.calledOnce(authSpy);
//输出:AssertError:应调用一次remove,但调用了0次
//AssertError:应调用一次setAuthorizationToken,但调用了0次
cookieSpy.restore();//void
导入setAuthorizationToken

我认为这是一个经典的模块,所以我不明白为什么我要使用
authSpy.restore();


你的两个间谍实际上有两个不同的补丁,都有相同的潜在问题。
sinon.spy(someFunction)
实际上并不包装
someFunction
本身,它为它返回一个间谍,但不执行任何替换

对于第一个spy,存在一个自动包装对象方法的速记:
sinon.spy(cookie,'remove')
应该执行您需要的操作

对于第二个spy,它更为复杂,因为您需要将spy包装在默认导出的
setAuthorizationToken
周围。为此,您需要类似proxyquire的东西。proxyquire是一种特殊的require机制,允许您使用所需的测试方法替换导入。以下是您需要做的简要说明:

const authSpy = sinon.spy(setAuthorizationToken);
bindTokenWithApp = proxyquire('./middleware', { './setAuthorizationToken': authSpy});

你的两个间谍实际上有两个不同的补丁,都有相同的潜在问题。
sinon.spy(someFunction)
实际上并不包装
someFunction
本身,它为它返回一个间谍,但不执行任何替换

对于第一个spy,存在一个自动包装对象方法的速记:
sinon.spy(cookie,'remove')
应该执行您需要的操作

对于第二个spy,它更为复杂,因为您需要将spy包装在默认导出的
setAuthorizationToken
周围。为此,您需要类似proxyquire的东西。proxyquire是一种特殊的require机制,允许您使用所需的测试方法替换导入。以下是您需要做的简要说明:

const authSpy = sinon.spy(setAuthorizationToken);
bindTokenWithApp = proxyquire('./middleware', { './setAuthorizationToken': authSpy});

感谢您的解释。我尝试了这两种解决方案,但都不起作用。对于我现在测试的第一个间谍,我理解了潜在的问题:
sinon.spy(cookies,'remove');assert.calledOnce(cookies.remove);
,但输出相同。对于第二个间谍,我尝试了proxyquire和jest.mock(从这里开始,但没有成功)。我不能断言
bindTokenWithApp=proxyquire…
,因为它是从导入中只读的。当我断言
const bindTokenWithApp=…
时,我不能执行它,因为它不是一个函数…谢谢你的解释。我已经尝试了两种解决方案,但都不起作用。我理解潜在的问题,这是我现在遇到的第一个间谍我的测试:
sinon.spy(cookies,'remove');assert.calledOnce(cookies.remove);
但输出相同。对于第二个spy,我尝试了proxyquire和jest.mock(从这个测试中,但没有成功)。我无法断言
bindTokenWithApp=proxyquire…
,因为它是从导入读取的。当我断言
const bindTokenWithApp=…
时,我无法执行它,因为它不是函数…