Sinon.restore não funciona para criar stub e testar funções AWS
Portanto, estou tentando escrever alguns testes para testar uma biblioteca de wrapper da AWS que venho escrevendo. Os testes estão sendo executados individualmente sem nenhum problema, mas nem todos serão executados como um bloco de 'descrição'.
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;
});
...
O que percebi, é que no segundo teste, o erro que estou recebendo é sqs.receiveMessage is not a function
, o que significa que o segundo teste está usando o sqs
objeto do primeiro teste (posso verificar isso posteriormente, pois o erro muda se eu adicionar receiveMessage
ao primeiro sqs
objeto de teste )
Este é um bug na restauração do sinon ou escrevi algo incorretamente? Aqui está toda a biblioteca:https://github.com/unegma/aws-utilities/blob/main/test/SQSTests.spec.js
Respostas
Este não é um problema com a Sinon. Este é um problema de como você está criando o SDK do AWS. Vamos analisar o que está acontecendo no código que você compartilhou.
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;
Este código faz o seguinte
- Esboço
SQS
deaws
. - Carregar
AWSUtilities.js
(com base no código-fonte no github)
AWSUtilities.js
faz o seguinte assim que é carregado
const aws = require('aws-sdk');
const sqs = new aws.SQS();
// code removed to demo the concept
O código acima cria um sqs
objeto interno , que neste caso é feito usando o módulo aws stubbed. No nó, uma vez que um módulo é carregado, require
ele é armazenado em cache na memória, ou seja, o código acima é executado apenas uma vez.
Portanto, quando o primeiro é it()
executado, ele carrega AWSUtilities.js
pela primeira vez e é armazenado em cache. Qualquer chamada subsequente obtém a versão em cache. Quando você o chama sinon.restore
apenas restaura a SQS
função do aws
módulo, ele não restaura o sqs
objeto que foi criado dentro AWSUtilities.js
.
Espero que isso explique o motivo do comportamento que você está vendo.
Existem várias maneiras de corrigir esse problema. Injeção de dependência, usando módulos como proxyquire, rewire, stubbing aws de um local central antes de todos os casos de teste, etc.
A seguir está uma opção para corrigi-lo apenas nos casos de teste mostrados aqui.
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;
});
});