针对全局 window 的多个 Sinon 存根未按预期工作
Multiple Sinon stubs against global window not working as expected
我将 Karma 与 Mocha、Chai 和 Sinon 结合使用来测试使用 this boilerplate. The Subject Under Test uses the Speech Synthesis API 的项目中的代码。
我首先在 beforeEach
方法中建立 window.speechSynthesis.getVoices
beforeEach(() => {
global.window.speechSynthesis = {
getVoices: () => (null),
};
});
然后我有两个测试用例,在每个测试用例中,我想测试当返回一组不同的声音时会发生什么。为此,我使用 Sinon stubs
第一个测试用例
it('supports speech and locale', () => {
const getVoicesStub = sinon.stub(
global.window.speechSynthesis,
'getVoices');
getVoicesStub.callsFake(() => (
[{lang: 'en_US'}]
));
第二个测试用例
it('will choose best matching locale', () => {
const getVoicesStub = sinon.stub(
global.window.speechSynthesis,
'getVoices');
getVoicesStub.callsFake(() => (
[{lang: 'es_MX'}, {lang: 'es_US'}]
));
问题是,当 SUT 在第二个测试用例中调用 window.speechSynthesis.getVoices
时,它从第一个存根中获取结果。好像第二个存根什么也没做...
如果我注释掉第一个测试用例,第二个测试用例会成功,但如果我把它们都留在里面,第二个测试用例会失败,因为返回了错误的声音集。
知道如何让第二个存根按预期工作吗?
你的存根在测试之间没有被破坏。您需要在测试后恢复默认功能,并在 before
中仅创建一次存根
describe("Test suite", () => {
let getVoicesStub;
before(() => {
// executes before suite starts
global.window.speechSynthesis = {
getVoices: () => (null),
};
getVoicesStub = sinon.stub(global.window.speechSynthesis, 'getVoices');
});
afterEach(() => {
// executes after each test
getVoicesStub.restore();
});
it('supports speech and locale', () => {
getVoicesStub.callsFake(() => ([{lang: 'en_US'}]));
});
it('will choose best matching locale', () => {
getVoicesStub.callsFake(() => ([{lang: 'es_MX'}, {lang: 'es_US'}]));
});
});
首先,感谢@Troopers。只需添加此答案即可分享我一路上注意到的最终解决方案和细节。
真正的诀窍是添加一个测试套件级变量 let getVoicesStub
,然后为 restore
原始函数
定义一个 afterEach
方法
afterEach(() => {
getVoicesStub.restore();
});
@Troopers 关于在 before
方法中定义存根的建议的微妙警告 -
如果存根是在测试用例之外定义的,我必须使用beforeEach
,如果存根是在测试用例内定义的,我必须使用before
方法。
在这两种情况下,afterEach
都很关键!我选择了 beforeEach
解决方案,因为存根仅在一个地方定义,因此代码略少。
describe('Browser Speech', () => {
let getVoicesStub;
beforeEach(() => {
global.window.speechSynthesis = {
getVoices: () => (null),
};
getVoicesStub = sinon.stub(
global.window.speechSynthesis,
'getVoices');
});
afterEach(() => {
getVoicesStub.restore();
});
it('supports speech and locale', () => {
getVoicesStub.callsFake(() => (
[{lang: 'en_US'}]
));
// test case code ..
});
it('will choose best matching locale', () => {
getVoicesStub.callsFake(() => (
[{lang: 'es_MX'}, {lang: 'es_US'}]
));
// test case code ..
});
});
我将 Karma 与 Mocha、Chai 和 Sinon 结合使用来测试使用 this boilerplate. The Subject Under Test uses the Speech Synthesis API 的项目中的代码。
我首先在 beforeEach
方法中建立 window.speechSynthesis.getVoices
beforeEach(() => {
global.window.speechSynthesis = {
getVoices: () => (null),
};
});
然后我有两个测试用例,在每个测试用例中,我想测试当返回一组不同的声音时会发生什么。为此,我使用 Sinon stubs
第一个测试用例
it('supports speech and locale', () => {
const getVoicesStub = sinon.stub(
global.window.speechSynthesis,
'getVoices');
getVoicesStub.callsFake(() => (
[{lang: 'en_US'}]
));
第二个测试用例
it('will choose best matching locale', () => {
const getVoicesStub = sinon.stub(
global.window.speechSynthesis,
'getVoices');
getVoicesStub.callsFake(() => (
[{lang: 'es_MX'}, {lang: 'es_US'}]
));
问题是,当 SUT 在第二个测试用例中调用 window.speechSynthesis.getVoices
时,它从第一个存根中获取结果。好像第二个存根什么也没做...
如果我注释掉第一个测试用例,第二个测试用例会成功,但如果我把它们都留在里面,第二个测试用例会失败,因为返回了错误的声音集。
知道如何让第二个存根按预期工作吗?
你的存根在测试之间没有被破坏。您需要在测试后恢复默认功能,并在 before
describe("Test suite", () => {
let getVoicesStub;
before(() => {
// executes before suite starts
global.window.speechSynthesis = {
getVoices: () => (null),
};
getVoicesStub = sinon.stub(global.window.speechSynthesis, 'getVoices');
});
afterEach(() => {
// executes after each test
getVoicesStub.restore();
});
it('supports speech and locale', () => {
getVoicesStub.callsFake(() => ([{lang: 'en_US'}]));
});
it('will choose best matching locale', () => {
getVoicesStub.callsFake(() => ([{lang: 'es_MX'}, {lang: 'es_US'}]));
});
});
首先,感谢@Troopers。只需添加此答案即可分享我一路上注意到的最终解决方案和细节。
真正的诀窍是添加一个测试套件级变量 let getVoicesStub
,然后为 restore
原始函数
afterEach
方法
afterEach(() => {
getVoicesStub.restore();
});
@Troopers 关于在 before
方法中定义存根的建议的微妙警告 -
如果存根是在测试用例之外定义的,我必须使用beforeEach
,如果存根是在测试用例内定义的,我必须使用before
方法。
在这两种情况下,afterEach
都很关键!我选择了 beforeEach
解决方案,因为存根仅在一个地方定义,因此代码略少。
describe('Browser Speech', () => {
let getVoicesStub;
beforeEach(() => {
global.window.speechSynthesis = {
getVoices: () => (null),
};
getVoicesStub = sinon.stub(
global.window.speechSynthesis,
'getVoices');
});
afterEach(() => {
getVoicesStub.restore();
});
it('supports speech and locale', () => {
getVoicesStub.callsFake(() => (
[{lang: 'en_US'}]
));
// test case code ..
});
it('will choose best matching locale', () => {
getVoicesStub.callsFake(() => (
[{lang: 'es_MX'}, {lang: 'es_US'}]
));
// test case code ..
});
});