Unit testing promises in controllers in AngularJS

╄→尐↘猪︶ㄣ 提交于 2019-12-11 04:47:29

问题


We recently started incorporating promises in our controllers to handle multiple requests at the same time and it works in the app, but unit testing these has been proven to be more than difficult and I will I have a hard time grasping exactly what it is I'm missing. Following are two snippets that are very simplified on what I'm exactly trying to test.

Controller:

angular.module('testengine').controller('testController', [
  '$scope', 
  '$q', 
  'Api', 
  function($scope, $q, Api) {
    $scope.getTest = function (deferred) {

      // do stuff...

      Api.test.get(params, function(response) {
        // do stuff with response

        if (deferred) {
          deferred.resolve(); // if success
        }
      });

    };

    $scope.runTest = function () {

       // do stuff...

       var promises = [];
       var deferred = $q.defer();

       promises.push(deferred.promise);
       $scope.getTest(deferred);

       $q.all(promises).then(function () {
         $scope.navigate();
       }, function () {
         // do stuff with rejected
       });

    };

    $scope.navigate = function () {
      // do stuff
    };
  }]);

Test:

describe('Controller:', function () {

  var root, scope, controllerFactory, $q;

  function createController () {
    return controllerFactory('testController', {
      $scope: scope
    });
  }

  beforeEach(function () {
    module('testengine');
  });

  beforeEach(inject(function ($rootScope, $controller, _$q_) {
    root = $rootScope;
    scope = $rootScope.new();
    controllerFactory = $controller;
    $q = _$q_;
  });

  it('should run test', function () {
    createController();

    var deferred = $q.defer();
    spyOn(scope, 'getTest').andReturn(deferred.resolve());
    spyOn(scope, 'navigate');

    $scope.runTest();

    root.$apply();
    expect(scope.navigate).toHaveBeenCalled();
  });
});

According to all examples and documentation I've read on promises and $q, this should work, but it doesn't, instead I get:

Expected spy navigate to have been called but it was never called.

I'm guessing because it's not the same deferred object I'm resolving in my mocked spy, but how am I suppose to mock that? Do I have to bind the deferred object on the controller or the scope or what?

The reason for the structure you see is because there are different methods using getTest() (and some do not only use that request but also others, hence promises). Also, getTest() is tested separately in another test, which is why I want to mock that function and any requests made in it.

Any help is appreciated and if I've made some blatant errors, I'm happy to educate myself being still fairly new to Angular.


回答1:


Yes, you are not resolving the right promise. You need to intercept the function arguments.

spyOn(scope, 'getTest');
expect(scope.getTest).toHaveBeenCalled();
scope.getTest.mostRecentCall.args[0].resolve();

This is a good concise Jasmine reference: http://tobyho.com/2011/12/15/jasmine-spy-cheatsheet/



来源:https://stackoverflow.com/questions/22986782/unit-testing-promises-in-controllers-in-angularjs

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!