'nodejs: Tests pass individually, fail when run together (mocha, sinon, aws-sdk)

I have tests that:

  1. PASS when run together in VS Code
  2. FAIL when run together on the CLI
  3. PASS when run individually (e.g. mocha bogus.test.js -g "second")

Here's the code as bare bones as I can make it (and it's pretty darned bare bones):

const AWS = require("aws-sdk")
const SQS = new AWS.SQS()

exports.handler = () => {
  return SQS.deleteMessage({foo: "bar"}).promise()
}

and the test:

const sinon = require("sinon")
const expect = require("chai").expect
const AWS = require("aws-sdk")

describe("Bogus Test", () => {
  let sandbox, deleteMessageStub

  beforeEach(() => {
    sandbox = sinon.createSandbox()

    deleteMessageStub = sandbox.fake.returns({ promise: () => Promise.resolve({}) })
    sandbox.stub(AWS, 'SQS').returns({
      deleteMessage: deleteMessageStub
    })  
  })

  afterEach(() => { sandbox.restore() })

  it("is the first test", () => {
    const bogus = require("../bogus")
    return bogus.handler().then(() => {
      expect(deleteMessageStub.callCount).to.equal(1, 'Should have called deleteMessage once')
    })
  })

  it("is the second test", () => {
    const bogus = require("../bogus")
    return bogus.handler().then(() => {
      expect(deleteMessageStub.callCount).to.equal(1, 'Should have called deleteMessage once')
    })
  })
})

Results:

Bogus Test
✓ is the first test
1) is the second test


1 passing (14ms)
1 failing

1) Bogus Test
is the second test:

Should have called deleteMessage once
+ expected - actual

-0
+1

at ... bogus.test.js:29:46

I would be DELIGHTED to find out that I'm doing something stupid...



Solution 1:[1]

require a module multiple times, the code of the module scope will only be executed once, because the module is cached in the require.cache object.

Which means the ./bogus module is only loaded once and is obtained from the require.cache object during the second require. The const SQS = new AWS.SQS(); statement will be executed only once.

You cleared the call information of the stub through ·sinon.restore()·, so the stub.callCount of the second test case is 0.

Solution: Clear the module cache of ./bogus within beforeEach() hook

E.g.

bogus.js:

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

exports.handler = () => {
  return SQS.deleteMessage({ foo: 'bar' }).promise();
};

bogus.test.js:

const sinon = require('sinon');
const expect = require('chai').expect;
const AWS = require('aws-sdk');

describe('Bogus Test', () => {
  let sandbox, deleteMessageStub;

  beforeEach(() => {
    sandbox = sinon.createSandbox();

    deleteMessageStub = sandbox.fake.returns({ promise: () => Promise.resolve({}) });
    sandbox.stub(AWS, 'SQS').returns({
      deleteMessage: deleteMessageStub,
    });
    delete require.cache[require.resolve('./bogus')];
  });

  afterEach(() => {
    sandbox.restore();
  });

  it('is the first test', () => {
    const bogus = require('./bogus');
    return bogus.handler().then(() => {
      expect(deleteMessageStub.callCount).to.equal(1, 'Should have called deleteMessage once');
    });
  });

  it('is the second test', () => {
    const bogus = require('./bogus');
    return bogus.handler().then(() => {
      expect(deleteMessageStub.callCount).to.equal(1, 'Should have called deleteMessage once');
    });
  });
});

unit test result:

  Bogus Test
    ? is the first test
    ? is the second test


  2 passing (9ms)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1