2

In my NodeJS app I've got the following heplers.ts file with one method, wait:

export const wait = async (ms: number) => {
  return new Promise(resolve => {
    setTimeout(resolve, ms);
  });
};

I'm currently writing a unit test to this file and this is what I have now:

import { setProcessEnv } from 'spec/helpers';
import { wait } from '../../util/helpers';

describe('Helper methods', () => {
  beforeEach(() => {
    jasmine.DEFAULT_TIMEOUT_INTERVAL = 20000;
    setProcessEnv();
  });

  it('should wait a specified amount of time', async () => {
    const TIMEOUT = 10000;
    jasmine.clock().install();  // First install the clock

    await wait(TIMEOUT);

    jasmine.clock().tick(10000); // waits till 10000 milliseconds

    expect(wait).toHaveBeenCalled();
    
    jasmine.clock().uninstall(); // uninstall clock when done
  });
  
});

But I'm constantly receiving

Error: Timeout - Async function did not complete within 20000ms (set by jasmine.DEFAULT_TIMEOUT_INTERVAL)

I've increased jasmine.DEFAULT_TIMEOUT_INTERVAL to 20000 ms, but it still didn't work. How such async functions could be tested?

I've found coupes examples, but they didn't work in my case: How to test a function which has a setTimeout with jasmine?

UPDATE

This is my latest version that doesn't throw an error, but the problem is that it doesn't cover return statement lines (return new Promise(...):

it('should wait a specified amount of time', async () => {
     const TIMEOUT = 10000;

    // jasmine.clock().install();  // First install the clock
    const wait = jasmine.createSpy();

    await wait(TIMEOUT);

    // jasmine.clock().tick(1000); // waits till 10000 milliseconds
    expect(wait).toHaveBeenCalled();
    expect(wait).toHaveBeenCalledWith(TIMEOUT);
    
    // jasmine.clock().uninstall(); // uninstall clock when done
  });
Karen
  • 1,249
  • 4
  • 23
  • 46
  • your test seems to need more than 20 seconds to execute, it demands 10 secs to execute `await wait(TIMEOUT);`, another 10 sec for `jasmine.clock().tick(10000);`. What happens when you set `jasmine.DEFAULT_TIMEOUT_INTERVAL = 22000`? – ilkerkaran Oct 05 '20 at 08:16

1 Answers1

2

The problem is that by using jasmine's custom clock you need to manually call .tick() to move the time forward. Since you immediately await the wait call, the setTimeout within wait will not be reached. Thus, the promise never resolves, as you call .tick() after awaiting the promise.

You can fix this by not immediately awaiting the the promise returned from wait but instead assigning it to a variable and then moving the time ahead. Then, await the promise and check if wait has been called:

describe('Helper methods', () => {

    it('should wait a specified amount of time', async () => {
        const TIMEOUT = 10000;
        jasmine.clock().install();  // First install the clock

        const waitPromise =  wait(TIMEOUT);
        jasmine.clock().tick(10000); // waits till 10000 milliseconds

        await waitPromise; // now await the promise

        expect(wait).toHaveBeenCalled();

        jasmine.clock().uninstall(); // uninstall clock when done
    });
});

Note that in the above code no spy has been set up on wait, so .toHaveBeenCalled will probably fail. Check this link if you need help setting up a spy on a function: https://stackoverflow.com/a/43532075/3761628


To answer the updated question: You've incorrectly set up the spy. You need to change it to something like:

import { setProcessEnv } from 'spec/helpers';
import * as WaitFunction from from '../../util/helpers';
...
it('should wait a specified amount of time', async () => {
    const TIMEOUT = 10000;    
    
    const waitSpy = spyOn(WaitFunction, 'wait').and.callThrough();

    await WaitFunction.wait(TIMEOUT);

    expect(waitSpy).toHaveBeenCalled();
    expect(waitSpy).toHaveBeenCalledWith(TIMEOUT);
    
  });
eol
  • 23,236
  • 5
  • 46
  • 64
  • sorry eol, but it doesn't make any sense to do it like the solution that you've provided - it constantly throws an error – Karen Oct 05 '20 at 08:29
  • Why wouldn't it make sense? "it constantly throws an error" - *what* is the error now? – eol Oct 05 '20 at 08:33
  • `wait` is not a spy, after I changed it to spy, it doesn't cover the whole method :( I've updated the post – Karen Oct 05 '20 at 08:44
  • Well `wait` not being a spy is exactly what I told you in the last sentence of my answer. – eol Oct 05 '20 at 08:52
  • Also you're creating the spy incorrectly.. I've updated my answer to show you how.. – eol Oct 05 '20 at 09:13
  • thank you for the updated answer, it really worked - the problem is that tests are still not covering 2 and 3 lines of the method (return Promise) statement, is Jasmine wants to check the return value? – Karen Oct 05 '20 at 09:20
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/222520/discussion-between-eol-and-karen). – eol Oct 05 '20 at 10:01