Sinon.restore ne fonctionne pas pour le stubbing et le test des fonctions AWS

Nov 26 2020

J'essaie donc d'écrire quelques tests pour tester une bibliothèque de wrapper AWS que j'ai écrite. Les tests s'exécutent individuellement sans aucun problème, mais ne s'exécutent pas tous comme un seul bloc «décrire».

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

...

Ce que j'ai remarqué, c'est que dans le deuxième test, l'erreur que j'obtiens est sqs.receiveMessage is not a function, ce qui signifie que le deuxième test utilise l' sqsobjet du premier test (je peux le vérifier davantage à mesure que l'erreur change si j'ajoute receiveMessageau premier sqsobjet de test ).

Est-ce un bogue dans sinon restore, ou ai-je écrit quelque chose de manière incorrecte? Voici toute la bibliothèque:https://github.com/unegma/aws-utilities/blob/main/test/SQSTests.spec.js

Réponses

1 Josnidhin Dec 16 2020 at 10:59

Ce n'est pas un problème avec Sinon. Il s'agit d'un problème lié à la façon dont vous stubbing AWS SDK. Décrivons ce qui se passe dans le code que vous avez partagé.

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;

Ce code fait ce qui suit

  1. Stub SQSde aws.
  2. Load AWSUtilities.js(basé sur le code source dans github)

AWSUtilities.js fait ce qui suit dès qu'il est chargé

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

// code removed to demo the concept

Le code ci-dessus crée un sqsobjet interne , qui dans ce cas est créé à l'aide du module aws stubbed. Dans node une fois qu'un module est chargé en utilisant requiresa mémoire cache, c'est-à-dire que le code ci-dessus ne s'exécute qu'une seule fois.

Ainsi, lorsque le premier it()s'exécute, il se charge à son tour AWSUtilities.jspour la première fois et est mis en cache. Tous les appels ultérieurs obtiennent la version mise en cache. Lorsque vous l'appelez, sinon.restoreil ne restaure que la SQSfonction du awsmodule, il ne restaure pas l' sqsobjet qui a été créé à l'intérieur AWSUtilities.js.

J'espère que cela explique la raison du comportement que vous voyez.

Il existe plusieurs façons de résoudre ce problème. Injection de dépendances, en utilisant des modules comme proxyquire, rewire, stubbing aws à partir d'un emplacement central avant tous les cas de test, etc.

Ce qui suit est une option pour résoudre ce problème uniquement dans les cas de test présentés ici.

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