Sinon.restoreがAWS関数のスタブとテストで機能しない

Nov 26 2020

そのため、私が作成しているAWSラッパーライブラリをテストするためのいくつかのテストを作成しようとしています。テストは問題なく個別に実行されますが、すべてが1つの「describe」ブロックとして実行されるわけではありません。

const AWS_REGION = 'eu-west-2';

const aws = require('aws-sdk');
const chai = require('chai');
const expect = chai.expect;
const sinon = require('sinon');
const sinonChai = require('sinon-chai');
chai.use(sinonChai);

// These help:
// https://stackoverflow.com/questions/26243647/sinon-stub-in-node-with-aws-sdk
// https://stackoverflow.com/questions/61516053/sinon-stub-for-lambda-using-promises
describe('SQS Utilities Test', () => {

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

  it('should add to SQS', async () => {
    sinon.stub(aws.config, 'update');

    const sqs = {
      sendMessage: sinon.stub().returnsThis(),
      promise: sinon.stub()
    };
    sinon.stub(aws, 'SQS').callsFake(() => sqs);

    // these use the above stubbed version of aws
    const AWSUtilities = require('../index').AWSUtilities;
    const awsUtilities = new AWSUtilities(AWS_REGION);
    const response = await awsUtilities.postToSQS('https://example.com', { id: 1}, 'chicken');

    expect(sqs.sendMessage).to.have.been.calledOnce;
  });

  it('should get from SQS', async () => {
    sinon.stub(aws.config, 'update');

    const sqs = {
      receiveMessage: sinon.stub().returnsThis(),
      promise: sinon.stub()
    };
    sinon.stub(aws, 'SQS').callsFake(() => sqs);

    // these use the above stubbed version of aws
    const AWSUtilities = require('../index').AWSUtilities;
    const awsUtilities = new AWSUtilities(AWS_REGION);

    const response = await awsUtilities.getFromSQS('https://example.com');
    expect(sqs.receiveMessage).to.have.been.calledOnce;
  });

...

私が気付いたのは、2番目のテストで得られるエラーはですsqs.receiveMessage is not a function。これは、2番目のテストがsqs最初のテストのオブジェクトを使用していることを意味します(receiveMessage最初のテストsqsオブジェクトに追加するとエラーが変化するため、これをさらに確認できます) )。

これはsinon復元のバグですか、それとも間違って書いたのですか?ライブラリ全体は次のとおりです。https://github.com/unegma/aws-utilities/blob/main/test/SQSTests.spec.js

回答

1 Josnidhin Dec 16 2020 at 10:59

これはSinonの問題ではありません。これは、AWSSDKをスタブする方法の問題です。共有したコード内で何が起こっているかを分析してみましょう。

const sqs = {
  sendMessage: sinon.stub().returnsThis(),
  promise: sinon.stub()
};
sinon.stub(aws, 'SQS').callsFake(() => sqs);

// these use the above stubbed version of aws
const AWSUtilities = require('../index').AWSUtilities;

このコードは次のことを行います

  1. スタブSQSaws
  2. ロードAWSUtilities.js(githubのソースコードに基づく)

AWSUtilities.js ロードされるとすぐに次のことを行います

const aws = require('aws-sdk');
const sqs = new aws.SQS();

// code removed to demo the concept

上記のコードは内部sqsオブジェクトを作成します。この場合、これはスタブされたawsモジュールを使用して作成されます。ノードでは、モジュールがrequireメモリにキャッシュされていることを使用してロードされると、上記のコードは1回だけ実行されます。

したがって、最初にit()実行すると、順番にロードさAWSUtilities.jsれ、キャッシュされます。以降の呼び出しでは、キャッシュされたバージョンが取得されます。これを呼び出すsinon.restoreSQSawsモジュールの機能のみが復元sqsされ、内で作成されたオブジェクトは復元されませんAWSUtilities.js

それがあなたが見ている行動の理由を説明していることを願っています。

この問題を修正する方法は複数あります。すべてのテストケースの前に、proxyquire、rewire、中央の場所からawsをスタブするなどのモジュールを使用した依存性注入。

以下は、ここに示されているテストケースだけでそれを修正するためのオプションです。

describe('SQS Utilities Test', () => {
  let AWSUtilities, sqsStub;

  before(() => {
    sinon.stub(aws.config, 'update');

    sqsStub = {
      sendMessage: sinon.stub().returnsThis(),
      receiveMessage: sinon.stub().returnsThis(),
      promise: sinon.stub()
    };
    sinon.stub(aws, 'SQS').callsFake(() => sqs);
    AWSUtilities = require('../index').AWSUtilities;
  });

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

  it('should add to SQS', async () => {
    const awsUtilities = new AWSUtilities(AWS_REGION);
    const response = await awsUtilities.postToSQS('https://example.com', { id: 1}, 'chicken');
    expect(sqsStub.sendMessage).to.have.been.calledOnce;
  });

  it('should get from SQS', async () => {
    const awsUtilities = new AWSUtilities(AWS_REGION);
    const response = await awsUtilities.getFromSQS('https://example.com');
    expect(sqsStub.receiveMessage).to.have.been.calledOnce;
  });
});