Sinon.restore 不适用于存根和测试 AWS 函数

Sinon.restore not working for stubbing and testing AWS functions

所以我正在尝试编写一些测试来测试我一直在编写的 AWS 包装器库。 测试单独 运行ning 没有任何问题,但不会全部 运行 作为一个 '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:
// 
// 
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 restore 的错误,还是我写错了什么?这是整个图书馆:https://github.com/unegma/aws-utilities/blob/main/test/SQSTests.spec.js

这不是 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. aws 的存根 SQS
  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 加载模块,它就会缓存在内存中,即上面的代码只执行一次。

所以当第一个it()执行时它会依次加载AWSUtilities.js并被缓存。任何后续调用都会获取缓存版本。当您调用 sinon.restore 时,它只会恢复 aws 模块的 SQS 函数,不会恢复在 AWSUtilities.js.[=26 中创建的 sqs 对象=]

我希望这能解释您所看到的行为的原因。

有多种方法可以解决此问题。依赖注入,使用诸如 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;
  });
});