AWS 함수 스터 빙 및 테스트에 대해 Sinon.restore가 작동하지 않음

Nov 26 2020

그래서 저는 제가 작성한 AWS 래퍼 라이브러리를 테스트하기위한 몇 가지 테스트를 작성하려고합니다. 테스트는 문제없이 개별적으로 실행되지만 모두 하나의 '설명'블록으로 실행되지는 않습니다.

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

...

내가 알아 차린 것은 두 번째 테스트에서 내가 얻는 오류 sqs.receiveMessage is not a function는입니다. 즉, 두 번째 테스트가 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의 문제가 아닙니다. 이것은 AWS SDK를 스터 빙하는 방법에 대한 문제입니다. 공유 한 코드 내에서 무슨 일이 일어나고 있는지 분석해 보겠습니다.

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객체를 생성하며 ,이 경우에는 stubbed aws 모듈을 사용합니다. 노드에서 모듈이로드되면 require메모리에 캐시됩니다. 즉, 위 코드는 한 번만 실행됩니다.

따라서 첫 번째가 it()실행 되면 처음 으로로드 AWSUtilities.js되고 캐시됩니다. 모든 후속 호출은 캐시 된 버전을 가져옵니다. 호출 sinon.restore하면 모듈 의 SQS기능 만 복원하고 .NET에서 생성 된 객체는 aws복원하지 않습니다 .sqsAWSUtilities.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;
  });
});