Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sleep in Jasmine

Usecase: For my angularJS(1.6.4) application, I am writing unit test in jasmine.

In my test case, I call a poll() function which keeps calling another function CheckTaskStatus() repeatedly using $interval. I am spy-ing CheckTaskStatus() and want to check that it has been called a certain number of times. So after calling poll(), I want to be able to wait for some time and then check the the number of times CheckTaskStatus() is called using expect().

Problem: I have been unable to find a way to make jasmine wait after call to poll() and before expect().

After poll() I have tried using below options, but those do not cause jasmine to sleep:

  • $timeout
  • settimeout
  • async function which calls await on function returning promise. Service import * as angular from 'angular';

export class Polling { public static $inject = ['$q', '$http', '$interval'];

constructor(private $q: ng.IQService, private $http, private $interval) {}

public poll(interval: number, pollFn: () => ng.IPromise<any>, until?: (any) => boolean, cancel?: ng.IPromise<any>) {
    let intervalPromise = null;
    const done = this.$q.defer();

    const intervalFn = () => {
        pollFn().then((pollFnResult) => {
            if (until && until(pollFnResult)) {
                this.$interval.cancel(intervalPromise);
                done.resolve();
            }
        }).catch(() => {});  
    };

    // Set up an interval to execute the pollFunction
    intervalPromise = this.$interval(intervalFn, interval);
    intervalPromise.catch(() => {});

    intervalFn();

    if (cancel) {
        cancel.then(() => {
            this.$interval.cancel(intervalPromise);
            done.resolve();
        })
            .catch(() => {}); 
    }

    return done.promise;
}

}

export default angular.module('myPolling', []) .service('polling', Polling).name;

Jasmine test

fdescribe('myPolling module tests', () => {
    'use strict';
    let $rootScope,
        $q: ng.IQService,
        $http,
        $interval,
        $timeout;

    beforeEach(mockModule('myPolling'));
    beforeEach(() => {
        jasmine.clock().install();
    });
    beforeEach(inject((_$rootScope_, _$q_, _$http_, _$interval_, _polling_, _$timeout_) => {
        this.$rootScope = _$rootScope_;
        this.$q = _$q_;
        this.$http = _$http_;
        this.$interval = _$interval_;
        this.polling = _polling_;
        $timeout = _$timeout_;


    }));

    function sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

    async function SleepForASecond() {
        await sleep(1050);
    }

    it('blah', () => {

        let pollCount = 0;
        let pollObj = {
            pollFn: () => {
                pollCount++;
                return this.$q.when(pollCount);
            }
        };
         // jasmine.createSpy('pollFn');
        spyOn(pollObj, 'pollFn').and.callThrough();

        let cancel = this.$q.defer();
        this.polling.poll(100, pollObj.pollFn, () => false, cancel.promise);
		
		// Need a mechanism to wait for a second.
		// SleepForASecond().then(() => { cancel.resolve(); });
		
        expect(pollObj.pollFn).toHaveBeenCalledTimes(10);

    });

});
like image 950
Phalgun Avatar asked Oct 18 '25 18:10

Phalgun


1 Answers

AngularJS asynchronous code and arbitrary JS code are tested differently in Jasmine.

AngularJS services were designed specifically to make tests synchronous, including $interval:

In tests you can use $interval.flush(millis) to move forward by millis milliseconds and trigger any functions scheduled to run in that time.

So it is:

let cancel = this.$q.defer();
this.polling.poll(100, pollObj.pollFn, () => false, cancel.promise);

$interval.flush(1050);
expect(pollObj.pollFn).toHaveBeenCalledTimes(10);

It's different in tests that involve non-Angular asynchronous units. Jasmine clock API should be used instead. It patches built-in timer functions in order to execute them synchronously with Jasmine tick method.

beforeEach(() => {
  jasmine.clock().install();
});

afterEach(() => {
  jasmine.clock().uninstall();
});

it('...', () => {
  SleepForASecond().then(() => {
    expect(1).toBe(0);
  });
  jasmine.clock().tick(1050);
});

Jasmine 2.7 adds support for promises and async functions. This allows to seamlessly test promise-based functions, but promises will produce a real delay and result in asynchronous test:

it('...', async () => {
  await SleepForASecond();
  expect(1).toBe(0);
});
like image 72
Estus Flask Avatar answered Oct 22 '25 04:10

Estus Flask



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!