How to conditionally flush a $timeout in Angular unit test

后端 未结 2 1879
醉酒成梦
醉酒成梦 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.

提交回复
热议问题