Sinon mock 正在调用真正的 AWS Secrets Manager

Sinon mock is calling real AWS Secrets Manager

我正在尝试模拟 AWS Secrets Manager 的 getSecretValue 方法。

它不起作用 - 它没有调用 sinon 模拟,而是调用了真正的 AWS 函数。

示例测试:

'use strict';

const sinon = require( 'sinon' );
const AWS = require( 'aws-sdk' );
const secretsManager = new AWS.SecretsManager();
const unitUnderTest = require( '../modules/myUnitUnderTest' );
const { expect } = require( 'chai' );

describe( 'Failing example for Stack Overflow', async () => {

    afterEach( () => {
        sinon.restore();
    } );

    it( 'mocks the call to AWS Secret Manager successfully', async () => {

        sinon.stub( secretsManager, 'getSecretValue' ).returns( Promise.resolve( {SecretString: { publicKey: 'secretUsername', privateKey: 'secretPassword' }} ) );
        const key = 'key';
        const username = null;
        const password = null;
        await unitUnderTest( key, username, password );
        expect( username ).to.equal( 'secretUsername' );
        expect( password ).to.equal( 'secretPassword' );
    } );
} );

示例函数:

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

module.exports = async ( keyId, username, password ) => {
        await getSecret( keyId )
            .then( secret => {
                username = secret.publicKey;
                password = secret.privateKey;
            } )
            .catch(
                err => {
                    logger.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 ) );
            }
        } );
    } );
};

预期行为:

Sinon 嘲笑 AWS Secrets Manager

实际行为:

Sinon 不工作,AWS 被真正调用,我在日志中收到错误,证明真正的 AWS SDK 已被 invoked/has 尝试从 AWS Secrets Manager 帐户中读取我的秘密我机器上的默认配置文件:

Failed to obtain the secret: ConfigError: Missing region in config

我对此束手无策。为什么 sinon 不工作?

由于您是在模块范围内实例化 AWS.SecretsManager,因此您需要在需要模块之前对 AWS.SecretsManager class 进行存根。

例如

main.js:

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

main.test.js:

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

describe('67322634', () => {
  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,
    );
  });
});

单元测试结果:

  67322634
    ✓ should get secret value (1472ms)


  1 passing (1s)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |   85.71 |       50 |   83.33 |   85.71 |                   
 main.js  |   85.71 |       50 |   83.33 |   85.71 | 12,24             
----------|---------|----------|---------|---------|-------------------