Testing $interval in Jasmine/ Karma
Asked Answered
R

1

5

I have a simple factory

angular.module('myApp.dice',[]).factory('Dice', ['$interval', function($interval){
    return {
      rollDice: function(){
        return $interval(function(c){
               count++;
          }, 100, 18, false, 0);
      }
    };
}]);

In my test case I have

describe('rolling dice', function(){
      var promise, promiseCalled = false, notify = false;
      beforeEach(inject(function(){
        promise = Dice.rollDice();
        promise.then(function(){
          promiseCalled = true;
        });

      }));

      it('should invoke a promise', inject(function(){
        expect(promise).toBeDefined();
      }));

      it('should set promiseCalled', inject(function($rootScope, $interval){


        expect(promiseCalled).toEqual(true);
      }));
    });

How do I trigger the interval or resolve the promise? to get the test to be true?

Regent answered 24/5, 2016 at 0:56 Comment(0)
L
9

See plunker

You need to use '$interval.flush' from angularjs-mocks and then test for the result. I've taken the liberty to assign count to the dice object because it's undefined in your code sample.

Is there any reason why $interval is called with invokeApply = false? because then you have to manually call $apply.

The test case would be:

describe('rolling dice', function(){
    var $interval, $rootScope, dice;

    beforeEach(module('plunker'));

    beforeEach(inject(function(_$interval_, _$rootScope_, _Dice_){
      $interval = _$interval_;
      $rootScope = _$rootScope_;
      dice = _Dice_;
    }));

    it('should increment count', inject(function(){
      dice.rollDice();
      // Move forward by 100 milliseconds
      $interval.flush(100);
      // Need to call apply because $interval was called with invokeApply = false
      $rootScope.$apply();
      expect(dice.count).toBe(1);
    }));
  });

with factory:

app.factory('Dice', ['$interval', function($interval){
    var self= {
      count: 0,
      rollDice: function(){
        return $interval(function() {
              self.count++;
          }, 100, 18, false, 0);
      }
    };
    return self;
}]);

EDIT: I prefer testing the result of a function call but perhaps you have a use case for testing that a promise function is called so this might be what you're looking for. From the angular docs on $interval it says it returns

A promise which will be notified on each iteration.

Keyword being notified. And from the promise API the arguments for then are

then(successCallback, errorCallback, notifyCallback)

i.e. the third call back function notifyCallback is called for each iteration of $interval. So the test would look something like this:

it('should set promiseCalled', function(){
  var promiseCalled = false;
  dice.rollDice().then(null, null, function() {
    promiseCalled = true;
  });
  $interval.flush(100);
  $rootScope.$apply();
  expect(promiseCalled).toBe(true);
});

If you want to test for a resolved promise, then it would look something like this:

it('should resolve promise', function(){
  var promiseCalled = false;
  var resolvedData = null;
  $q.when('Some Data').then(function(data) {
    promiseCalled = true;
    resolvedData = data;
  });
  $rootScope.$apply();
  expect(promiseCalled).toBe(true);
  expect(resolvedData).toBe('Some Data');
});

I've updated the plunker with these test cases.

Lagerkvist answered 24/5, 2016 at 2:13 Comment(3)
Aaaahhhh....thanks. In previous code I had $interval.flush but the $rootScope.$apply is something new....Regent
I just realise that this question while solves a problem does not answer a pertinent question.... How do I test the promise callback event return from $interva()Regent
@Regent Updated answer to address your last commentLagerkvist

© 2022 - 2024 — McMap. All rights reserved.