简体   繁体   中英

How to test if function A was called inside the test function B if it was called asynchronously

So basically I have the function I want to test which we will call function A. I want to test if function B was called inside function A. The problem is that function B is being called inside function A asynchronously through a resolved Promise. This will cause sinon assertion to fail because the test will finish before function B is called!

Here is a working code scenario.

const sinon = require('sinon');

describe('functionToBeTested', () => {
  it('someFunction is called', () => {
    // spy on the function we need to check if called
    const spy = sinon.spy(someClass.prototype, 'someFunction');
    // call the function being tested
    functionToBeTested();
    // check if spy was called
    sinon.assert.called(spy);
  });
});

class someClass {
  someFunction() {
    console.log('Hello');
  }
}

const somePromise = Promise.resolve();

function functionToBeTested() {
  const instance = new someClass();
  // some synchronous code here
  // if instance.someFunction() is called here the test will pass
  // .
  // .
  // .
  somePromise.then(() => {
    instance.someFunction();
    // the function is called and Hello is printed but the test will fail
  })
  // .
  // .
  // .
  // some synchronous code here
  // if instance.someFunction() is called here the test will pass
} 

Your example is a bit unconventional. You have functionToBeTested which has a dual behavior (sync and async at the same time). When you put this method under test, the behavior should be well known and standardized before hand, so that you can structure the test and assertions accordingly.

The problem in this scenario is that you try to validate the behavior of the function is sync mode, although the inner parts work in a fire-and-forget fashion - ie there is no dependency on the result of the instance.someFunction() method.

If functionToBeTested() returned a promise - thus being async by design, that would be straightforward for your test scenario. But in this case, you will need an unconventional testing approach as well. That means that if you do something like:

describe('functionToBeTested', () => {

    it('someFunction is called', (done) => {

        // spy on the function we need to check if called
        const spy = sinon.spy(SomeClass.prototype, 'someFunction');

        // call the function being tested
        functionToBeTested();

        setTimeout(() => {
            // check if spy was called
            sinon.assert.called(spy);
            done();
        }, 10);

    });
});    

the test would pass. What happened here is that we declared the test async by making use of the done argument in the callback. Also, we added a timer to mimic a delay prior to checking whether the spy was called.

Since the ' fire-and-forget ' call only prints out a message, waiting for 10ms would be enough. If the promise took longer to complete, the wait time should be adjusted.

As stated before, unconventional implementations require unconventional approaches. I would suggest that you reconsider your requirements and redesign the solution.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM