Sinon.restore() 不适用于命名空间类的继承函数

问题描述

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

驱动程序代码

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

这有什么问题吗?文档表明这应该没问题。

解决方法

@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

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...