Sinon.restore nie działa do tworzenia kodów kreskowych i testowania funkcji AWS

Nov 26 2020

Więc próbuję napisać kilka testów do testowania biblioteki AWS, którą pisałem. Testy przebiegają indywidualnie bez żadnych problemów, ale nie wszystkie będą działały jako jeden blok „opisywania”.

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

...

Zauważyłem, że w drugim teście otrzymuję błąd sqs.receiveMessage is not a function, co oznacza, że ​​drugi test wykorzystuje sqsobiekt z pierwszego testu (mogę to dalej zweryfikować, ponieważ błąd się zmienia, jeśli dodam receiveMessagedo pierwszego sqsobiektu testu ).

Czy to błąd w przywracaniu sinon, czy może napisałem coś niepoprawnie? Oto cała biblioteka:https://github.com/unegma/aws-utilities/blob/main/test/SQSTests.spec.js

Odpowiedzi

1 Josnidhin Dec 16 2020 at 10:59

To nie jest problem z Sinonem. To kwestia sposobu, w jaki korzystasz z pakietu AWS SDK. Podzielmy się tym, co dzieje się w udostępnionym kodzie.

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;

Ten kod wykonuje następujące czynności

  1. Stub SQSz aws.
  2. Załaduj AWSUtilities.js(na podstawie kodu źródłowego w github)

AWSUtilities.js wykonuje następujące czynności zaraz po załadowaniu

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

// code removed to demo the concept

Powyższy kod tworzy wewnętrzny sqsobiekt, który w tym przypadku jest tworzony za pomocą modułu stubbed aws. W węźle po załadowaniu modułu przy użyciu requirejego buforowania w pamięci, tj. Powyższy kod jest wykonywany tylko raz.

Więc kiedy pierwszy it()wykonuje, to z kolei ładuje AWSUtilities.jssię po raz pierwszy i jest buforowany. Każde kolejne wywołanie otrzymuje wersję z pamięci podręcznej. Kiedy wywołujesz sinon.restore, przywraca tylko SQSfunkcję awsmodułu, nie przywraca sqsobiektu, który został utworzony w środku AWSUtilities.js.

Mam nadzieję, że to wyjaśnia powód zachowania, które widzisz.

Istnieje wiele sposobów rozwiązania tego problemu. Wstrzykiwanie zależności, używanie modułów takich jak proxyquire, rewire, stubbing aws z centralnej lokalizacji przed wszystkimi przypadkami testowymi itp.

Poniżej znajduje się opcja naprawienia tego problemu tylko w pokazanych tutaj przypadkach testowych.

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