jamie holliday
jamie holliday

Reputation: 1627

Testing promises in angular controllers with Jasime

I am having a bit of trouble getting set up to test an Angular controller that contains promises.

The controller code it this:

angular.module('jhApp')
.controller('adminPagesCtrl', function(resourceCache) {
    var adminPages = this;
    adminPages.items = resourceCache.query('page');

    adminPages.delete = function(page) {
        resourceCache.delete('page', {id:page._id})
        .then(function(responceData) {
            if(responceData.deleted === true) {
                adminPages.items = resourceCache.query('page');
            }
        });
    };
});

my test looks like this:

describe('adminPagesCtrl', function() {
    var defferred,
        $rootScope,
        controller,
        resourceCache,
        scope,
        page,
        defferred,
        promise;

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

    beforeEach(inject(function ($rootScope, $controller, $q) {
        scope = $rootScope.$new();
        controller = $controller('adminPagesCtrl as adminPages', {$scope: scope});
        deffered = $q.defer();
        promise = deffered.promise;

        resourceCache = {
            delete: promise
        };
        page = {_id: 1};
        spyOn(resourceCache, 'delete');

    }));

    it('deletes a page', function() {
        expect(controller).toBeDefined();
        scope.adminPages.delete(page);
        console.log(resourceCache.delete) //outputs: function{}
        console.log($rootScope) //outputs: undefined
        resourceCache.delete.resolve({deleted: true});
        $rootScope.$apply();
        expect(resourceCache.delete).toHaveBeenCalled();
    });
});

I am trying to mock the resourceCache promise so it returns some fake data and so I can just test that something got returned and the adminPages.delete calls the the resourceCache.delete. I think I am doing something fundamentally wrong though as the current error is:

undefined is not a fuction

This i am sure is because if I try to log out resourceCache.delete it just shows and empty function. The first expect

resourceCache.delete.resolve();

passes ok.

Upvotes: 1

Views: 72

Answers (2)

jamie holliday
jamie holliday

Reputation: 1627

I finally got this working so in case anyone else has a similar issue here is the amended version. Calling $rootScope.digest() was causing an error with Karma.

Error: Unexpected request: GET views/projects.html
    No more request expected

I swapped that for scope = $rootScope.$new(); and now tests are passing.

describe('adminPagesCtrl', function() {
      var createController, $rootScope, deferred, resourceCache, scope;

      beforeEach(module('jhApp'));

      beforeEach(inject(function($controller, $rootScope, $q) {
        scope = $rootScope.$new();
            deferred = $q.defer();

        resourceCache = {
            delete: function () {

            },
            query: function (page) {

            }
        };

        spyOn(resourceCache, 'delete').and.returnValue(deferred.promise);

        createController = function() {
            return $controller('adminPagesCtrl', { resourceCache: resourceCache } );
        };
      }));

      it('deletes a page', function() {
        //Arrange
        var controller = createController();
        var page = {
          _id: 1
        };

        var response = {
          deleted: true
        };

        var items = [{
          test: 'test'
        }];

        var expectedDeleteParam = {
          id: page._id
        };

        spyOn(resourceCache, 'query').and.returnValue(items);

        //Act
        controller.delete(page);
        deferred.resolve(response);
        scope.$digest();

        //Assert
        expect(resourceCache.delete).toHaveBeenCalledWith('page', expectedDeleteParam);
        expect(resourceCache.query).toHaveBeenCalledWith('page');
        expect(controller.items).toEqual(items);
      });
    });

Upvotes: 0

Wayne Ellery
Wayne Ellery

Reputation: 7958

You need to setup resourceCache.delete as a function that returns a promise rather than just set to a promise. You should also be mocking resourceCache.query. To resolve the promise you need to use deffered.resolve(response); after controller.delete is called. Then $rootScope.$digest();.

describe('adminPagesCtrl', function() {
  var createController, $rootscope, deferred, resourceCache;

  beforeEach(module('jhApp'));

  beforeEach(inject(function($controller, _$rootScope_, $q) {
    $rootScope = _$rootScope_;
    deferred = $q.defer();

    resourceCache = {
      delete: function () {

      },
      query: function (page) {

      }
    };

    spyOn(resourceCache, 'delete').and.returnValue(deferred.promise);

    createController = function() {
            return $controller('adminPagesCtrl', { resourceCache: resourceCache } );
     };

  }));

  it('deletes a page', function() {
    //Arrange
    var controller = createController();
    var page = {
      _id: 1
    };

    var response = {
      deleted: true
    };

    var items = [{
      test: 'test'
    }];

    var expectedDeleteParam = {
      id: page._id
    };

    spyOn(resourceCache, 'query').and.returnValue(items);

    //Act
    controller.delete(page);
    deferred.resolve(response);
    $rootScope.$digest();

    //Assert
    expect(resourceCache.delete).toHaveBeenCalledWith('page', expectedDeleteParam);
    expect(resourceCache.query).toHaveBeenCalledWith('page');
    expect(controller.items).toEqual(items);
  });
});

Plunkr

Upvotes: 1

Related Questions