Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

179
Visualizações
Sinon ignores subsequent async call

I need to verify that all transaction calls are succeeded. Here's the example:

module.js

const functionUnderTest = async (helper) => {
  await helper.transaction(async (transaction) => {
    await helper.doSomething();
    await helper.doSomething2();
    await helper.expectedToBeCalled();
    console.log("done");
  });
};
module.exports = {
  functionUnderTest,
};

The problem comes, that for some reason with sinon I cannot verify that the last call was done at all, however the message "done" is always printed.

Here how output looks like


$ npm run test

> sinon-test@1.0.0 test
> mocha



  module
    ✔ the former function should be called
done
    1) the latter function should be called
done


  1 passing (7ms)
  1 failing

  1) module
       the latter function should be called:
     AssertionError: expected stub to have been called at least once, but it was never called
      at Context.<anonymous> (test/module.test.js:31:48)
      at processTicksAndRejections (node:internal/process/task_queues:96:5)

The tests itself are represented below

helpers/chai.js

const chaiAsPromised = require("chai-as-promised");
const chaiSinon = require("sinon-chai");

chai.use(chaiSinon);
chai.use(chaiAsPromised);

module.exports = { expect: chai.expect };

test/module.test.js

const { expect } = require("./helpers/chai");
const sinon = require("sinon");
const { functionUnderTest } = require("../module");

describe("module", function () {
  let sandbox;
  let mockHelper;
  const transaction = {};

  beforeEach(function () {
    sandbox = sinon.createSandbox();
    mockHelper = {
      transaction: sandbox.stub().callsArgWithAsync(0, transaction),
      doSomething: sandbox.stub().resolves({}),
      doSomething2: sandbox.stub().resolves({}),
      expectedToBeCalled: sandbox.stub().resolves({}),
    };
  });

  afterEach(function () {
    sandbox.restore();
  });

  it("the former function should be called", async function () {
    await functionUnderTest(mockHelper);
    expect(mockHelper.doSomething).to.be.called;
  });

  it("the latter function should be called", async function () {
    await functionUnderTest(mockHelper);
    expect(mockHelper.expectedToBeCalled).to.be.called;
  });
});

To make it complete and easy to test, I've created a repo that you can play with: https://github.com/vichugunov/sinon-test

My question are:

  • how is it possible?
  • what am I doing wrong?

P.S. If the call await helper.doSomething2() is commented out, tests are passing

about 4 years ago · Juan Pablo Isaza
1 Respostas
Responde à pergunta

0

You misunderstand async versions callsArg* and yields* for stubs. See this PR, the difference is async versions callsArg* will:

  • In Node environment the callback is deferred with process.nextTick.
  • In a browser the callback is deferred with setTimeout(callback, 0).

It does not wait for the asynchronous callback function to complete. async/await in the test case can only wait for the completion of the await helper.transaction() asynchronous function, not its asynchronous callback.

The sync version of callsArg* will call the callback immediately. You can take a look at this issue, when should use async versions callsArg*.

Therefore, when the test case performs an assertion, the asynchronous function such as helper.expectedTobecalled() is not completed.

You should use callsFake() to provide an async stub callback for helper.transaction() method and invoke it with async/await.

E.g.

module.js:

const functionUnderTest = async (helper) => {
  await helper.transaction(async (transaction) => {
    await helper.doSomething();
    await helper.doSomething2();
    await helper.expectedToBeCalled();
    console.log('done');
  });
};
module.exports = { functionUnderTest };

module.test.js:

const sinon = require('sinon');
const { functionUnderTest } = require('./module');

describe('module', function () {
  let sandbox;
  let mockHelper;
  const transaction = {};

  beforeEach(function () {
    sandbox = sinon.createSandbox();
    mockHelper = {
      transaction: sandbox.stub().callsFake(async (callback) => {
        await callback(transaction);
      }),
      doSomething: sandbox.stub().resolves({}),
      doSomething2: sandbox.stub().resolves({}),
      expectedToBeCalled: sandbox.stub().resolves({}),
    };
  });

  afterEach(function () {
    sandbox.restore();
  });

  it('the former function should be called', async function () {
    await functionUnderTest(mockHelper);
    sinon.assert.calledOnce(mockHelper.doSomething);
  });

  it('the latter function should be called', async function () {
    await functionUnderTest(mockHelper);
    sinon.assert.calledOnce(mockHelper.expectedToBeCalled);
  });
});

Test result:

  module
done
    ✓ the former function should be called
done
    ✓ the latter function should be called


  2 passing (5ms)
about 4 years ago · Juan Pablo Isaza Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda