Mocking resolve in state using Jasmine & Angular - angularjs

I'm trying to mock the resolve functions inside the $state of my ui-router file, but I can't seem to get it to work. Here's my router code:
$stateProvider
.state('state.of.page', {
url: '/url-to-page',
template: require('./page-template.html'),
controller: 'PageCtrl',
controllerAs: 'page',
resolve: {
/**
* Cloning of page object in case user leaves page
* without saving
*/
pageObjClone: ['pageObj', function (pageObj) {
return angular.copy(pageObj);
}],
pageTemplate: ['template', function (template) {
return template;
}]
}
Here is my Jasmine code. I'm currently getting the error 'fn' is not a function when I run the test.
'use strict';
describe('my jasmine $state test', function() {
var $state;
var $injector;
var stateName = 'state.of.page';
var stateObj;
beforeEach(function() {
angular.mock.module('MyApp');
inject(function(_$rootScope_, _$state_, _$injector_) {
$state = _$state_;
$injector = _$injector_;
stateObj = {
customerObjClone: ['customerObj', function (customerObj) {}],
template: ['template', function (template) {}]
};
});
});
it('should resolve data', function() {
var state = $state.get(stateName);
expect($injector.invoke($state.resolve)).toBe('stateObj');
});
});
Thanks for any help.

...
var pageObj, template;
beforeEach(function() {
angular.mock.module('MyApp');
inject(function(..., _pageObj_, _template_) {
...
pageObj = _pageObj_;
template = _template_;
});
});
it('should resolve data', function() {
var state = $state.get(stateName);
expect($injector.invoke(state.resolve.pageObjClone)).toEqual(pageObj);
expect($injector.invoke(state.resolve.pageTemplate)).toBe(template);
});
Depending on how complex resolvers and their dependencies are, the dependencies may be mocked and injected into resolver, e.g.
expect($injector.invoke(state.resolve.pageTemplate, null, { template: mockedTemplate }))
.toBe(mockedTemplate);

Related

Unit Test for controller in AngularJs Failed

Here is my controller:
'use strict';
angular.module('pulseOneApp')
.config(function ($stateProvider) {
$stateProvider.state('about', {
url: '/about',
views: {
'content': {
templateUrl: 'components/version/about.html',
controller: 'AboutController'
}
},
authNotRequired: true
});
})
.controller('AboutController', ['$scope', '$state', 'session', '$pulseOneProps', '$filter', 'propertiesServices', function ($scope, $state, session, $pulseOneProps, $filter, propertiesServices) {
/**
* #function getServerVersion
* #description gets the serverVersion from $pulseOneProps if exist, else makes a REST Api call using propertiesServices.
* #returns string
*/
var getServerVersion = function () {
var systemProperties,serverVersion;
if ((angular.isDefined($pulseOneProps)) && $pulseOneProps !== null) {
systemProperties = $pulseOneProps.getProperties();
if(systemProperties) {
return $filter('filter')(systemProperties, {name: 'server_version'})[0].value;
}
else{
//when the session exist and not able to retrieve $pulseOneProps then do REST Api call and update the systemProperties
session.validateSession().then(function() {
propertiesServices.getPulseOneProperties().then(function (systemProperties) {
serverVersion=$filter('filter')(systemProperties, {name: 'server_version'})[0].value;
// This will update the UI when serverVersion is available
$scope.serverVersion = (serverVersion) ? serverVersion: false;
});
});
}
}
return null; // if none of the above cases are valid then don't display the server version.
};
var serverVersion=getServerVersion();
$scope.serverVersion = (serverVersion) ? serverVersion: false;
$scope.goTo = function() {
session.validateSession().then(function() {
$state.go('app.dashboard');
})
.catch(function() {
$state.go('login');
});
};
}]);
and Here is my Unit Test for this controller to make sure the function goTo is the function:
'use strict';
describe('Controller: AboutCtrl', function () {
// load the controller's module
beforeEach(module('ui.router'));
beforeEach(module('ps.authentication.services'));
beforeEach(module('ps.version'));
beforeEach(module('pulseOneApp'));
beforeEach(module('ps.components.properties'));
var scope, AboutController;
// Initialize the controller and a mock scope
beforeEach(inject(function ($rootScope, _$controller_) {
scope = $rootScope.$new();
AboutController = _$controller_('AboutController', {
$scope: scope
});
scope.$digest();
}));
it('should find to goTo function', function () {
expect(typeof scope.goTo).toBe('function');
});
});
The unit test is failed and I don't know what was wrong with this unit test.
Any suggestion what was the issue here.
Note : The error message is : scope is undefined.
Thanks in advance
-k

Struggling to get started on AngularJS unit testing

I followed this post (http://gonehybrid.com/how-to-write-automated-tests-for-your-ionic-app-part-2/) to create a simple unit test using Karma & Jasmine for a Ionic controller, but i keep getting undefined errors while the stated objects have been defined. I'm i missing something obvious? By the way, i'm able to run referenced tests from the blog above successfully which makes me think i'm missing something in mine.
Errora are as follows:
TypeError: undefined is not an object (evaluating 'authMock.login') in /Users/projects/app/tests/unit-tests/login.controller.tests.js (line 65)
TypeError: undefined is not an object (evaluating 'deferredLogin.resolve') in /Users/projects/app/tests/unit-tests/login.controller.tests.js (line 71)
TypeError: undefined is not an object (evaluating 'deferredLogin.reject') in /Users/projects/app/tests/unit-tests/login.controller.tests.js (line 79)
Here's the controller:
angular.module('app').controller('LoginCtrl', function($scope, $state, $ionicPopup, $auth) {
$scope.loginData = {};
$scope.user = {
email: '',
password: ''
};
$scope.doLogin = function(data) {
$auth.login(data).then(function(authenticated) {
$state.go('app.tabs.customer', {}, {reload: true});
}, function(err) {
var alertPopup = $ionicPopup.alert({
title: 'Login failed!',
template: 'Please check your credentials!'
});
});
};
});
Here's the test:
describe('LoginCtrl', function() {
var controller,
deferredLogin,
$scope,
authMock,
stateMock,
ionicPopupMock;
// load the module for our app
beforeEach(angular.mock.module('app'));
// disable template caching
beforeEach(angular.mock.module(function($provide, $urlRouterProvider) {
$provide.value('$ionicTemplateCache', function(){} );
$urlRouterProvider.deferIntercept();
}));
// instantiate the controller and mocks for every test
beforeEach(angular.mock.inject(function($controller, $q, $rootScope) {
deferredLogin = $q.defer();
$scope = $rootScope.$new();
// mock dinnerService
authMock = {
login: jasmine.createSpy('login spy')
.and.returnValue(deferredLogin.promise)
};
// mock $state
stateMock = jasmine.createSpyObj('$state spy', ['go']);
// mock $ionicPopup
ionicPopupMock = jasmine.createSpyObj('$ionicPopup spy', ['alert']);
// instantiate LoginController
controller = $controller('LoginCtrl', {
'$scope': $scope,
'$state': stateMock,
'$ionicPopup': ionicPopupMock,
'$auth': authMock
});
}));
describe('#doLogin', function() {
// call doLogin on the controller for every test
beforeEach(inject(function(_$rootScope_) {
$rootScope = _$rootScope_;
var user = {
email: 'test#yahoo.com',
password: 'test'
};
$scope.doLogin(user);
}));
it('should call login on $auth Service', function() {
expect(authMock.login).toHaveBeenCalledWith(user);
});
describe('when the login is executed,', function() {
it('if successful, should change state to app.tabs.customer', function() {
deferredLogin.resolve();
$rootScope.$digest();
expect(stateMock.go).toHaveBeenCalledWith('app.tabs.customer');
});
it('if unsuccessful, should show a popup', function() {
deferredLogin.reject();
$rootScope.$digest();
expect(ionicPopupMock.alert).toHaveBeenCalled();
});
});
})
});
Here's my Karma config:
files: [
'../www/lib/ionic/js/ionic.bundle.js',
'../www/lib/angular-mocks/angular-mocks.js',
'../www/js/*.js',
'../www/js/**/*.js',
'unit-tests/**/*.js'
],
I think that your controller for tests is undefined. Try to replace first it function with this and check if is it defined.
it('controller to be defained', function() {
expect($controller).toBeDefined();
});
If it isn't, try to call controller with:
$controller = _$controller_;

Lazy loading angular services using require.js

I can lazy load a controller by doing the following,
Step1: Add an additional config...
rootModule.config([
"$controllerProvider", function($controllerProvider) {
rootModule.registerController = $controllerProvider.register;
}
]);
Step2: Define the controller against the registerController defined in step 1
angular.module("rootModule").registerController("authController",
function ($scope, $location, $rootScope, authService) {
$scope.userName = "";
$scope.userPwd = "";
$scope.authenticate = function ()...
$scope.testFunction = function ()...
});
Step3: load the controller during routing by doing this,
rootModule
.config([
'$routeProvider',
function ($routeProvider) {
$routeProvider.when('/',
{
templateUrl: 'templates/Login.html',
resolve: {
load: ["$q", function($q) {
var defered = $q.defer();
require(["Controllers/authController"], function() {
defered.resolve();
});
return defered.promise;
}]
}
}).
Now, the problem is I have a service called "authService", which I would like to lazy load, how to do it? Here is the service...
define(function() {
angular.module("rootModule").service("authService", function ($http) {
return {
/////Something code here//////
});
});
It was very simple in the end, thanks to this great blog written by Dan Wahlin.
To load a service in run time according to the routing, I had to do this...
Step 1: Get a reference to $provide.service() method in my rootModule's (module which contains the routing info) config...
rootModule.config(["$controllerProvider","$provide",
"$controllerProvider", "$filterProvider","$compileProvider", function ($controllerProvider, $provide) {
rootModule.registerController = $controllerProvider.register; //for controllers
rootModule.registerService = $provide.service; //for services
rootModule.registerFilter = $filterProvider.register; //for filters
rootModule.registerDirective = $compileProvider.directive; //for directives
rootModule.registerFactory = $provide.factory; //for factory
}
]);
Step 2: Register the service to be loaded dynamically
define(function() {
angular.module("rootModule").registerService("reviewReportsService", function () {
return {
sampleData: "This is some sample data"
}
});
});
Step 3: Resolve the service script file, to load when the respective route is loaded
when('/ReviewAndSubmit',
{
controller: "reviewAndSubmitController",
templateUrl: "templates/ReviewAndSubmit.html",
resolve: {
load: ["$q", function ($q) {
var defered = $q.defer();
require(["Controllers/reviewAndSubmitController"], function () {
defered.resolve();
});
require(["Services/reviewReportsService"], function () {
defered.resolve();
});
return defered.promise;
}]
}
})
Hope this helps someone....

How to unit test angularjs route's resolve with karma and mocha+chai?

I am working on an app where I need to resolve promises in the router (ngRoute). The problem is that I am not sure how to write the unit tests for this, I am using karma with mocha and chai.
Here is the part of the code I'd like to test:
function config ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/orders.html',
controller: 'OrderController',
controllerAs: 'vmr',
resolve: OrderController.resolve,
data: {...}
});
}
function OrderController (OrderService, newOrders) {
this.newOrders = newOrders;
}
OrderController.resolve = {
newOrders: function (OrderService) {
return OrderService.getOrders();
}
};
This is how I started to write my unit tests when I didn't have the resolve part yet:
describe('OrderController', function() {
'use strict';
var controller,
service,
httpBackend;
beforeEach(module('myApp.orders'));
beforeEach(inject(function($controller, _OrderService_, $httpBackend) {
service = _OrderService_;
httpBackend = $httpBackend;
// Create the controller
controller = $controller('OrderController', {});
}));
beforeEach(function() {
httpBackend.when('GET', 'url/to/get/orders')
.respond(200, {[...]});
});
afterEach(function() {
httpBackend.verifyNoOutstandingExpectation();
httpBackend.verifyNoOutstandingRequest();
});
it('should get the list of new orders', function() {
httpBackend.flush();
expect(controller.neworders).not.to.undefined;
expect(controller.neworders.length).to.equal(3);
});
});
At this point is where I am getting the error:
Unknown provider: newOrdersProvider <- newOrders
I understand why I get this error, but I don't know how to solve it. Basically I don't know how to test the promise that resolves in the route.
Thanks in advance for your help!
After a lot of searching and reading the AngularJS Testing Cookbook I find out how to inject the result of the promise in the controller.
The main code doesn't change, so I will post here only the update code for the unit tests:
describe('OrderController', function() {
'use strict';
var controller,
service,
httpBackend;
// here is where I will inject a new value
beforeEach(function() {
module('myApp.orders', function($provide) {
$provide.value('resolver', {
newOrders: function(service) {
return service.getOrders();
}
});
});
});
beforeEach(inject(function($controller, _OrderService_, $httpBackend, resolver) {
service = _OrderService_;
httpBackend = $httpBackend;
// Create the controller
controller = $controller('OrderController', {
// add them to the controller
newOrders: resolver.newOrders(service)
});
}));
beforeEach(function() {
httpBackend.when('GET', 'url/to/get/orders')
.respond(200, {[...]});
});
afterEach(function() {
httpBackend.verifyNoOutstandingExpectation();
httpBackend.verifyNoOutstandingRequest();
});
it('should get the list of new orders', function() {
httpBackend.flush();
expect(controller.neworders).not.to.undefined;
expect(controller.neworders.length).to.equal(3);
});
});
If someone has a better/different solution I'd like to hear it as well!

AngularJS changing controller mocks

I'm using this construct:
Directive with a ControllerAs.
The Controller has a depencency on a Service which does REST requests.
The directive and the controller:
angular.module('app')
.directive('thingsList', function () {
return {
templateUrl: 'thingsListEntry-template.html',
restrict: 'A',
controller: 'thingsListController as ctrl'
};
})
.controller('thingsListController', function (thingsStorage) {
thingsStorage.getList().then(angular.bind(this, function (response) {
this.things = response;
}));
});
What I want to do now is to test the directive with a controller mock:
describe('things list test suite', function() {
describe('tests for the directive', function () {
var scope, render, element, mockController;
/**
* Mock the controller
*/
beforeEach(module('app', function ($provide, $controllerProvider) {
$controllerProvider.register('thingsListController', function () {
this.things = [];
mockController = this;
});
}));
beforeEach(inject(function($rootScope, $compile) {
scope = $rootScope.$new();
var angularElement = angular.element('<div things-list></div>');
var compileFunction = $compile(angularElement);
render = function () {
element = compileFunction(scope);
$rootScope.$digest();
};
}));
it('should be empty without things', function() {
render();
expect(element[0].querySelectorAll('div.things-list-entry').length).toEqual(0);
});
What I would like to do next is to change the things in the controller mock and test that. I don't know how to do that
it('should contain 1 entry with 1 thing', function () {
mockController.things = [{'name':'1'}];
render();
expect(element[0].querySelectorAll('div.thing-list-entry').length).toEqual(1);
});
Here I'm setting mockController.things, but I'm not sure how to get to the mockController. The version above sets it in the mock setup. I also tried using scope.ctrl.things and couple other things but nothing works. Any suggestions?
Try scope.mockController.things instead of mockController.things.

Resources