How to conditionally clear $ timeout in Angular unit test

I have some code that looks something like this:

function doThing() {
  if (invalidInput) {
    console.error('Invalid input.');
    return;
  }

  $timeout(function() {
    MyService.doThing();
  }, 1000);
}

I want to check what is MyService.doThingnot called when passing invalid input.

If I call doThing(invalidInput)without doing $timeout.flush(), MyService.doThingit will not be called, regardless of whether I have lines 2-5. Therefore, in order to really check if MyService.doThingan invalid input is called when I enter, I need to call $timeout.flush.

The problem is that it throws an error if I try to flash when there is nothing to flush. Error: No deferred tasks to be flushed.

How can I handle this scenario? I would like to do something like $timeout.flushIfFlushable().

0
source share
2 answers

, doThing. :

(function () {
  'use strict';

  angular.module('myApp', [])
    .controller('MainCtrl', function ($timeout, MyService) {
      var vm = this;

      vm.invalidInput = true;
      vm.doThing = doThing;

      function doThing() {
        if (vm.invalidInput) {
          return;
        }

        $timeout(function () {
          MyService.doThing();
        }, 1000);
      }

    });
})();
(function () {
  'use strict';

  angular.module('myApp').service('MyService', MyService);

  function MyService() {

    this.doThing = function () {
      // doThing code
    };
  }
})();
  • Unit test
'use strict';

describe('Controller: MainCtrl', function () {

  beforeEach(module('myApp'));

  var vm,
    $timeout,
    MyService;

  beforeEach(inject(function (_$controller_, _$timeout_, _MyService_) {
    $timeout = _$timeout_;
    MyService = _MyService_;
    vm = _$controller_('MainCtrl', {
      $timeout: $timeout,
      MyService: MyService
    });
  }));

  it('should call doThing for valid inputs', function () {
    spyOn(MyService, 'doThing').andCallThrough();

    vm.invalidInput = false;
    vm.doThing();
    $timeout.flush();
    expect(MyService.doThing).toHaveBeenCalled();
  });

  it('should not call doThing for invalid inputs', function () {
    spyOn(MyService, 'doThing').andCallThrough();

    vm.invalidInput = true;
    vm.doThing();
    expect(MyService.doThing).not.toHaveBeenCalled();
  });

});

MyService.doThing(). , invalidInput true, .

, .

0

, , - $timeout .

: $timeout , ( , ).

beforeEach(module('app', ($provide) => {
  $provide.decorator('$timeout', ($delegate) => {
    var timeoutSpy = jasmine.createSpy().and.returnValue($delegate);
    // methods aren't copied automatically to spy
    return angular.extend(timeoutSpy, $delegate);
  });
}));

- false invalidInput:

...
MyService.doThing();
expect($timeout).not.toHaveBeenCalled();

- invalidInput:

...
MyService.doThing();
expect($timeout).toHaveBeenCalledWith(jasmine.any(Function), 1000);
$timeout.flush();
expect(MyService.doThing).toHaveBeenCalledTimes(2);

, promises :

function doThing() {
  if (invalidInput) {
    console.error('Invalid input.');
    return;
  }

  return $timeout(function() {
    return MyService.doThing();
  }, 1000);
}

, ( , ) .


, "flushIfFlushable()"

try {
  $timeout.verifyNoPendingTasks(); // just for semantics, not really necessary
  $timeout.flush();
} catch (e) {}

, .

0

Source: https://habr.com/ru/post/1695172/


All Articles