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.
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.
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.
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.
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.
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 });
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);
});
});
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';
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With