How to conditionally flush a $timeout in Angular unit test

后端 未结 2 1876
醉酒成梦
醉酒成梦 2021-01-27 16:24

I have code that is something like this:

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

  $timeout(function()         


        
相关标签:
2条回答
  • 2021-01-27 16:58

    No uncertainty is welcome in unit tests, it should be predictable whether there is something to flush for $timeout or not.

    In this piece of code two cases should be tested, in both $timeout shouldn't be a blackbox, it should be a stub instead (optionally a spy that wraps around the real service).

    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);
      });
    }));
    

    The first is falsey invalidInput:

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

    And the second one is truthy invalidInput:

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

    Disregarding this case, it is generally a good thing to return promises from promise-powered functions:

    function doThing() {
      if (invalidInput) {
        console.error('Invalid input.');
        return;
      }
    
      return $timeout(function() {
        return MyService.doThing();
      }, 1000);
    }
    

    This way the callers (most likely specs) may have some control on function's asynchronous behaviour.


    Answering the question directly, the expected way to do 'flushIfFlushable()' is

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

    Which should be avoided for the reasons listed above.

    0 讨论(0)
  • 2021-01-27 17:00

    I suggest define two separate unit tests to verify the behavior of your doThing function. Try following code:

    • Controller
    (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);
          }
    
        });
    })();
    
    • Service
    (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();
      });
    
    });
    

    With the first test we expect to call MyService.doThing() function. On other hand, if you have invalidInput as true, the previous function should not be called.

    I hope It helps.

    0 讨论(0)
提交回复
热议问题