Search code examples
javascriptunit-testingasynchronousmocha.jssinon

How to mock delay with sinon?


I have a method in a service that requires to wait 5000 ms exactly before returning. I need to unit test that method. I want to mock the delay using sinon fakeTimers because I don't want the entire unit test to actually wait 5000 ms. But I have 2 problems.

1) The test freezes as soon as I use sinon fakeTimers. I am looking for a way to tick 5000 ms when the code reaches the delay line.

2) How do I "assert" sure the value is returned exactly after 5000 ms, not 4999 ms, not 5001 ms ? (is that even possible ? )

class MyService {
    async doSomething(){

        await this.delay(5000)
        return 'Done'
    }
    delay(millis: number) {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve();
            }, millis);
        });
    }
}
describe('My Service', ()=>{
    it('should return done after 5s',async ()=>{
        const clock  = sinon.useFakeTimers(0)
        clock.tick(5000)
        const service = new MyService()
        const ans = await service.doSomething()
        expect(ans).to.equal('Done')
    })
})

Solution

  • You can check How to test async functions with fake timers doc for more info. Here is the solution:

    service.js:

    class MyService {
      async doSomething() {
        await this.delay(5000);
        return 'Done';
      }
      delay(millis: number) {
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            resolve();
          }, millis);
        });
      }
    }
    
    export { MyService };
    

    service.test.js:

    import { MyService } from './service';
    import sinon from 'sinon';
    import { expect } from 'chai';
    
    describe('61695981', () => {
      let clock;
      before(() => {
        clock = sinon.useFakeTimers();
      });
    
      after(() => {
        clock.restore();
      });
    
      it('should pass', async () => {
        const service = new MyService();
        const ansPromise = service.doSomething();
        clock.tick(5000);
        const ans = await ansPromise;
        expect(ans).to.equal('Done');
      });
    });
    

    unit test results with 100% coverage:

      61695981
        ✓ should pass
    
    
      1 passing (15ms)
    
    ------------|---------|----------|---------|---------|-------------------
    File        | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
    ------------|---------|----------|---------|---------|-------------------
    All files   |     100 |      100 |     100 |     100 |                   
     service.ts |     100 |      100 |     100 |     100 |                   
    ------------|---------|----------|---------|---------|-------------------