针对全局窗口的多个Sinon存根不能按预期工作

时间:2017-09-20 21:44:04

标签: javascript unit-testing mocha karma-runner sinon

我正在使用Karma与Mocha,Chai和Sinon一起使用this boilerplate测试项目中的代码。受测试者使用Speech Synthesis API。

我首先在window.speechSynthesis.getVoices方法

中建立beforeEach
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时,它会从第一个存根获得结果。好像第二根存根什么都没做......

如果我注释掉第一个测试用例,那么第二个测试用例就会成功,但是如果我把它们都留下来,第二个测试用例就会失败,因为错误的声音集会被返回。

知道如何让第二个存根按预期工作吗?

2 个答案:

答案 0 :(得分:1)

您的存根在测试之间不会被销毁。您需要在测试后恢复默认功能,并在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'}]));
    });
});

答案 1 :(得分:0)

首先,@Troopers的大人物。只需添加此答案即可分享最终解决方案和我在此过程中注意到的细节。

真正的诀窍是添加测试套件级变量let getVoicesStub,然后将afterEach方法定义为restore原始函数

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 ..
    });
});