【问题标题】:Sinon.restore() does not work for inherited functions of namespaced classesSinon.restore() 不适用于命名空间类的继承函数
【发布时间】:2021-08-12 19:15:13
【问题描述】:

Node AWS SDK 有一个名为 AWS 的命名空间,其中包含一个名为 SecretsManager 的类。 SecretsManager 继承了一个名为 getSecretValue 的函数定义。

我想在两个单独的单元测试中使用两个不同的结果来存根 AWS Secret Manager 的 getSecretValue 方法。

我编写了两个单独的单元测试,它们单独运行良好,但一起运行时失败。

sinon.restore() 不工作。

测试代码:

const AWS = require( 'aws-sdk' );
const sinon = require( 'sinon' );
const { expect } = require( 'chai' );

describe( '67672805', () => {
    afterEach( () => {
        sinon.restore();
    } );
    it( 'should get secret value', async () => {
        const data = {
            SecretString: JSON.stringify( { publicKey: 'secretUsername', privateKey: 'secretPassword' } ),
        };
        const secretsManagerStub = {
            getSecretValue: sinon.stub().callsFake( ( params, callback ) => {
                callback( null, data );
            } ),
        };
        const SecretsManagerStub = sinon.stub( AWS, 'SecretsManager' ).returns( secretsManagerStub );
        const main = require( './main' );
        const { username, password } = await main( '1' );
        expect( username ).to.equal( 'secretUsername' );
        expect( password ).to.equal( 'secretPassword' );
        sinon.assert.calledOnce( SecretsManagerStub );
        sinon.assert.calledOnceWithExactly(
            secretsManagerStub.getSecretValue,
            {
                SecretId: '1',
            },
            sinon.match.func,
        );
    } );

    it( 'should not get secret value if there is an error with SecretsManager', async () => {
        const secretsManagerStub = {
            getSecretValue: sinon.stub().callsFake( ( params, callback ) => {
                const err = new Error( 'There was an error' );
                callback( err );
            } ),
        };
        const SecretsManagerStub = sinon.stub( AWS, 'SecretsManager' ).returns( secretsManagerStub );
        const main = require( './main' );
        const { username, password } = await main( '1' );
        expect( username ).to.not.equal( 'secretUsername' );
        expect( password ).to.not.equal( 'secretPassword' );
        sinon.assert.calledOnce( SecretsManagerStub );
        sinon.assert.calledOnceWithExactly(
            secretsManagerStub.getSecretValue,
            {
                SecretId: '1',
            },
            sinon.match.func,
        );
    } );
} );

驱动代码:

const AWS = require( 'aws-sdk' );
const secretsManager = new AWS.SecretsManager();

module.exports = async ( keyId ) => {
    return getSecret( keyId )
        .then( ( secret ) => {
            const username = secret.publicKey;
            const password = secret.privateKey;
            return { username, password };
        } )
        .catch( ( err ) => {
            console.error( err );
        } );
};

const getSecret = ( keyId ) => {
    return new Promise( ( resolve, reject ) => {
        secretsManager.getSecretValue(
            {
                SecretId: keyId,
            },
            ( err, data ) => {
                if ( err ) {
                    reject( err );
                } else {
                    resolve( JSON.parse( data.SecretString ) );
                }
            },
        );
    } );
};

Package.json:

"aws-sdk": "2.894.0",
"chai": "4.3.4",
"sinon": "10.0.0"

预期:sinon.restore() 有效

实际:sinon.restore() 未正确重置 - 第二个单元测试失败,因为它使用在第一个单元测试中设置的相同存根。如果您颠倒测试顺序,它们会以相反的方式失败。如果您单独运行测试,它们就会通过。

这有什么问题?文档建议这应该没问题。

【问题讨论】:

  • 什么版本的诗浓?您可能还想查看this blog post,尽管我不知道这是否是您的问题。
  • @DaveNewton 很好,谢谢,我刚刚为问题添加了版本

标签: node.js sinon


【解决方案1】:

@fatso83 在 Sinon github 页面上回答了这个问题:https://github.com/sinonjs/sinon/issues/2376#issuecomment-847258040 并花时间在此处发布此问题的一个分支及其解决方案:https://github.com/fatso83/sinon-2376

此问题是由范围引起的。而不是在 main.js 顶部的模块范围内声明 AWS 机密管理器:

const AWS = require( 'aws-sdk' );
const secretsManager = new AWS.SecretsManager(); // module scope

应该在函数范围内声明存根项,如下所示:

const getSecret = ( keyId ) => {
    return new Promise( ( resolve, reject ) => {
        const secretsManager = new AWS.SecretsManager() // function scope
        secretsManager.getSecretValue(
            {
                SecretId: keyId,
            },
            ( err, data ) => {
                if ( err ) {
                    reject( err );
                } else {
                    resolve( JSON.parse( data.SecretString ) );
                }
            },
        );

这会导致测试通过,因为 AWS Secrets Manager 的模块范围实例不会在测试的调用中保留(而是在每次调用函数时被新的函数范围实例替换)。

TLDR:不要使用 SinonJS 存根模块范围的依赖关系

感谢@fatso83

【讨论】:

    猜你喜欢
    • 2017-12-10
    • 2017-12-22
    • 2013-03-10
    • 1970-01-01
    • 1970-01-01
    • 2013-08-13
    • 2017-12-21
    • 1970-01-01
    • 2017-09-27
    相关资源
    最近更新 更多