Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I test a controller with resolve properties in AngularJS?

How can one test a controller with resolve properties? It throws an error: Unknown provider: InitProvider, during testing, understandably. How can I test it?

I use the init property in the route config to load data and pass it along to the controller at controller instantiation so the route doesn't change before data is loaded.

  $routeProvider
    .when('/topic/:topic_id/content/:content_id', {
      templateUrl: 'views/content.html',
      controller: 'ContentCtrl',
    resolve: {
      init: ContentCtrl.init
    }
    });

Is the pattern completely wrong in the first place?

'use strict';

var ContentCtrl = ['$scope', '$location', '$routeParams', 'init', function ($scope, $location, $routeParams, init) {

    $scope.contents = init.contents;

  }];

ContentCtrl.init = ['$q', 'app_config', '$log', '$timeout', function ($q, app_config, $log, $timeout) {

    var defer = $q.defer();

    $log.log("ContentCtrl loading..");

    $timeout(function() {
        defer.resolve({contents: [
                                    {message: 'Hello!'}
                                  ]});

        $log.log("ContentCtrl loaded.");

    }, 2000);

    return defer.promise;
}];

angular.module('studentportalenApp').controller('ContentCtrl', ContentCtrl);

I want to encapsulate the whole controller inside .controler('ContentCtrl', function() { ... }), but have yet to figure out how this is done correctly to make the init available in the route configuration.

like image 908
Kenneth Lynne Avatar asked Mar 10 '13 12:03

Kenneth Lynne


People also ask

How to test directive AngularJS?

If a directive creates its own scope and you want to test against it, you can get access to that directive's scope by doing var directiveScope = myElement. children(). scope() - It will get the element's child (the directive itself), and get the scope for that.

How to do unit testing in AngularJS?

Testing in AngularJS is achieved by using the karma framework, a framework which has been developed by Google itself. The karma framework is installed using the node package manager. The key modules which are required to be installed for basic testing are karma, karma-chrome-launcher ,karma-jasmine, and karma-cli.

How controller works in AngularJS?

The controller in AngularJS is a JavaScript function that maintains the application data and behavior using $scope object. You can attach properties and methods to the $scope object inside a controller function, which in turn will add/update the data and attach behaviours to HTML elements.

How is view integrated to controller in Angular?

The simplest way to connect a controller to the view is to statically link the two with a directive in the HTML markup as shown here. This directive instructs AngularJS to instantiate the technologyController, defined in the JavaScript file of the same name, and use the model it defines for databinding.


3 Answers

Ran into the same thing here. I solved it using the approach here: https://groups.google.com/forum/?fromgroups=#!topic/angular/LzXm-9nwkjY.

Basically, I mocked the data that would normally be sent by using a simple variable and added it to the controller in the test. In your case I assume it would look something like:

var initData = {
      contents: [{message: 'Hello!'}]
};
$controller("ContentCtrl", { $scope: ..., init: initData });
like image 58
EGeuens Avatar answered Nov 08 '22 18:11

EGeuens


It was eventually solved by converting everything to services, as suggested by charlietfl.

Example:

Route config:

//This helper injects a function with the service 
//defined in the initMethod string and returns services.prepare()
var interceptWith = function(initMethod) {
        return [initMethod, function(m) {
                    return m.prepare();
                }];
}

$routeProvider        
    .when('/foobar/', {
        templateUrl: 'foobar.html',
        controller: 'FoobarCtrl',
        resolve: {
            init: interceptWith('FoobarCtrlInit')
        }
    });

The foobar controller definition:

angular.module('fooApp').controller('FoobarCtrl', ['$scope', 'init', function ($scope, init) {              
            $scope.data = init.data;    
  }])
.service('FoobarCtrlInit', ['$q', '$timeout', function ($q, $timeout) {

        var _prepare = function() {

            var deferred = $q.defer();

            //Fake async loading of data
            $timeout(function() {
                 deferred.resolve({data: ['A','B','C']});
            }, 1000);




            return deferred.promise; 
        }

        return {
            prepare: _prepare
        }
}]);

To test this, one could do this:

'use strict';

describe('Controller: FoobarCtrl', function() {

  // load the controller's module
  beforeEach(module('fooApp'));

  var FoobarCtrl,
    scope;

  // Initialize the controller and a mock scope
  beforeEach(inject(function($controller) {
    scope = {};
    CourseCtrl = $controller('FoobarCtrl', {
      $scope: scope,
      init: {data: ['Testdata A', 'B', 'C']}
    });
  }));

  it('should attach a list of data to the scope', function() {
    expect(scope.data.length).toBe(3);
  });
});
like image 36
Kenneth Lynne Avatar answered Nov 08 '22 20:11

Kenneth Lynne


I had the same error in Karma when using resolve on $routeProvider, I fixed it by testing my resolve in the unit test for app.js, like this:

describe("myApp",  function() {

  beforeEach(module('myApp'));

  it('should resolve initial values for my Controller', inject(function( $route ) {
    expect($route.routes['/'].resolve.init).toBeDefined; //or whatever test you want
  }));
});

And then I just mocked the value on the test for my controller, like this inside the describe for the controller:

//mock out the resolved values to isolate controller code
  beforeEach(module(function($provide) {
    $provide.value('init', function() {
      return 'whatever data you need to mock';
    });
like image 36
Meli Avatar answered Nov 08 '22 20:11

Meli