12
votes

I am trying to test a controller which uses angular's $resource.

function PermissionsCtrl($scope, $resource, $cookies) {
  var Object = $resource('/v1/objects/:id');
  loadObjects();

  function loadObjects() {
    $scope.myAppObjects = new Array();

    var data = AppObject.get({find: '{"app_id": '+wtm.app_id+'}'},
      function(){
        if (data.results) {
          for(var i = 0; i< data.results.length; i++) {
            if(!data.results[i].is_deleted) {
              (function(i){
                $scope.objects(data.results[i]);
              }(i));
            }
          }
        }
      },
      function(error){console.log(error);});
  }

And here is the test code.

var apiServer = "...";
var app_id = 999
var mock_object_data = {...};


describe('apps permissionsCtrl', function(){
  var scope, ctrl, $httpBackend;

  // Create a matcher for comparing data 
  beforeEach( function() {
    this.addMatchers({
      toEqualData: function(expected) {
        return angular.equals(this.actual, expected);
      }
    });
  });

  // Create the controller with injected data/services
  beforeEach(inject(function(_$httpBackend_, $rootScope, $controller, $resource) {
    $httpBackend = _$httpBackend_;
    // cookie data to inject
    var cookies = new Array();
    cookies['id'] = '...'; // just needs to be declared


    $httpBackend.expectGET(apiServer+'/v1/app_objects?    find=%7B%22app_id%22:'+app_id+'+%7D&access_token=' + cookies['id'])
      .respond( mock_object_data );

    var $injector = angular.injector(['ng', 'ngResource']);
    var $resource = $injector.get('$resource');

    scope = $rootScope.$new();
    ctrl = $controller(PermissionsCtrl, {$scope: scope, $cookies: cookies, $resource: $resource});
  }));

  it('should put object data into $scope', function() {
    $httpBackend.flush();
    expect(scope.objects).toEqualData( mock_object_data );

}); });

When I run this I get

Error: Unknown provider: $resourceProvider <- $resource

at the line where I try to create my controller. I don't understand how to inject this into my controller and no matter what I try I get the same error. A couple things I've tried are

  • Declaring a an empty mock object and passing it through similar to my cookies variable. I figure this is probably a bad solution anyway since I actually want to use the service.
  • Mimicking the scope mock and passing it into my inject function and passing $resource.$new() to my controller.
  • Doing nothing and hoping that httpBackend would cover it since that's what ultimately gets called anyway. Vojta Jína made it sound like that would work but no dice.
  • Mild epithets. Satisfying but not very effective.
2

2 Answers

7
votes

After more reading and more experimenting it seems the right way to do this is to abstract the use of $resource out of the controller. In my case I wrote a service that relies on $resource and then inject that service into my controller. Meanwhile I test that service separately from my controller. Better practice all around.

My service declaration:

angular.module('apiModule', ['localResource', 'ngCookies'])
.factory('apiService', function($resource, $cookies) {

and in my unit tests I pass it through in a beforeEach setup function

beforeEach(module('apiModule'));
6
votes

Try to instantiate $resource with the following code:

var $injector = angular.injector(['ng', 'ngResource']);
var $resource = $injector.get('$resource');

Similar for other services, except when they are in other modules still. Then add that module to the array.

More info