I'm trying to create unit tests to test a navigation list controller, and I'm having trouble creating tests.
Here is the code for the controller.
navListModule.controller('NavListCtrl', ['$scope', 'NavList', function ($scope, NavList) { $scope.$on('$routeChangeSuccess', function (event, routeData) { var stationId = routeData.params.stationId; if ((stationId !== null) && (stationId !== undefined)) { $scope.stationId = stationId; var navList = NavList; $scope.menuOptions = navList.getMenuOptions(stationId); } }); } ]);
Here is what I have so far used in my unit tests.
'use strict'; describe('unit testing navListModule', function () { var scope, ctrl, location; describe('test NavListCtrl', function () { beforeEach(module('shipApp.navListModule')); // mock NavListService for testing purposes var mockNavListService = { getMenuOptions: function (stationId) { // set default menu options var menuOptions = [ { name: "Alerts" , pageURL: "alerts" } , { name: "Reports" , pageURL: "reports" } , { name: "Run Close Outs" , pageURL: "closeOuts" } ]; // add admin menu option if stationId set to Admin if (stationId.toUpperCase() == 'Admin'.toUpperCase()) { menuOptions.push( { name: "Admin" , pageURL: "admin" } ); } return menuOptions; } }; beforeEach(inject(function ($rootScope, $controller, $location) { scope = $rootScope.$new(); ctrl = $controller('NavListCtrl', { $scope: scope, NavList: mockNavListService }); location = $location; })); it('should expect stationId to be undefined if stationId not defined in route parameters', function () { expect(scope.stationId).toBeUndefined(); }); it('should expect scope.$on not to be called if no change in route', function () { spyOn(scope, '$on'); expect(scope.$on).not.toHaveBeenCalled(); }); it('should expect scope.$on to be called on change in route', function () { spyOn(scope, '$on'); scope.$on('$routeChangeSuccess', function (event, routeData) {}); expect(scope.$on).toHaveBeenCalled(); }); it('should expect stationId to be defined in route parameters if route is #/:stationId/path', inject(function ($routeParams) { location.path('/Admin/alerts'); var locationElements = location.path().substring(location.path().indexOf('/') + 1).split('/'); var stationId = locationElements[0]; $routeParams.stationId = stationId; expect($routeParams.stationId).toEqual('Admin'); })); it('should expect menuOptions array to be returned when getMenuOptions function is called', function () { var stationId = 'Admin'; var menuOptions = NavListCtrl.getMenuOptions(stationId); }); }); });
I'm just learning Angular, so I'm not sure if I'm setting up the tests correctly. Should I create tests so that the controller logic does not execute until the $ routeChangeSuccess event occurs? If so, how can I write such a test? Also, how to test getMenuOptions call (last test) correctly? Please let me know how to properly test this controller.
Thanks in advance, Sean
After playing with some testing and some help from jvandemo, this is what I came up with for unit tests for the controller as well as for the base service. Please let me know if I am doing something wrong.
'use strict'; describe('unit testing navListModule', function () { beforeEach(module('shipApp.navListModule')); /***** Controllers *****/ describe('test NavListCtrl', function () { var ctrl, scope, NavList, $httpBackend, $location, $route, $routeParams; // mock the http backend for routing beforeEach(module(function() { return function(_$httpBackend_) { $httpBackend = _$httpBackend_; $httpBackend.when('GET', 'views/alerts/alerts.html').respond('alerts'); $httpBackend.when('GET', 'views/alerts/reports.html').respond('reports'); $httpBackend.when('GET', 'views/alerts/closeOuts.html').respond('closeOuts'); $httpBackend.when('GET', 'views/alerts/admin.html').respond('admin'); $httpBackend.when('GET', 'views/shared/error.html').respond('not found'); }; })); // add $routeProvider mock beforeEach(module(function ($routeProvider) { $routeProvider.when('/:stationId/alerts', { templateUrl : 'views/alerts/alerts.html', controller : 'AlertsCtrl' }); $routeProvider.when('/:stationId/reports', { templateUrl : 'views/reports/reports.html', controller : 'ReportsCtrl' }); $routeProvider.when('/:stationId/closeOuts', { templateUrl : 'views/closeOuts/closeOuts.html', controller : 'CloseOutsCtrl' }); $routeProvider.when('/:stationId/admin', { templateUrl : 'views/admin/admin.html', controller : 'AdminCtrl' }); $routeProvider.when('/404', { templateUrl : 'views/shared/error.html', controller : 'ErrorCtrl' }); $routeProvider.when('/', { redirectTo : '/MasterPl/alerts' }); $routeProvider.when('/:stationId', { redirectTo : '/:stationId/alerts' }); $routeProvider.when(':stationId', { redirectTo : '/:stationId/alerts' }); $routeProvider.when('', { redirectTo : '/MasterPl/alerts' }); $routeProvider.otherwise({ redirectTo: '/404' }); })); beforeEach(inject(function ($rootScope, $controller, _$location_, _$route_, _$routeParams_) { // mock NavList service var mockNavListService = { getMenuOptions: function (stationId) { // set default menu options var menuOptions = [ { name: "Alerts" , pageURL: "alerts" } , { name: "Reports" , pageURL: "reports" } , { name: "Run Close Outs" , pageURL: "closeOuts" } ]; // add admin menu option if stationId set to Admin if (stationId.toUpperCase() == 'Admin'.toUpperCase()) { menuOptions.push( { name: "Admin" , pageURL: "admin" } ); } return menuOptions; } }; NavList = mockNavListService; scope = $rootScope.$new(); $location = _$location_; $route = _$route_; $routeParams = _$routeParams_; ctrl = $controller('NavListCtrl', { $scope: scope, $routeParams: $routeParams, NavList: NavList }); })); it('should expect stationId and menuOptions to be undefined if stationId not defined in route parameters', function () { expect(scope.stationId).toBeUndefined(); expect(scope.menuOptions).toBeUndefined(); }); it('should expect scope.$on not to be called if no change in route', function () { spyOn(scope, '$on'); expect(scope.$on).not.toHaveBeenCalled(); }); it('should expect scope.$on to be called on change in route', function () { spyOn(scope, '$on'); scope.$on('$routeChangeSuccess', function (event, routeData) {}); expect(scope.$on).toHaveBeenCalled(); }); it('should not parse $routeParameters before $routeChangeSuccess', function () { $location.path('/Admin/alerts'); scope.$apply(); expect(scope.stationId).toBeUndefined(); }); it('should expect scope values to be set after $routeChangeSuccess is fired for location /stationId/path', function () { $location.path('/Admin/alerts'); scope.$apply(); $httpBackend.flush(); expect(scope.stationId).toEqual('Admin'); expect(scope.menuOptions).not.toBeUndefined(); }); it('should expect NavList.getMenuOptions() to have been called after $routeChangeSuccess is fired for location /stationId/path', function () { spyOn(NavList, 'getMenuOptions').andCallThrough(); $location.path('/Admin/alerts'); scope.$apply(); $httpBackend.flush(); expect(NavList.getMenuOptions).toHaveBeenCalled(); expect(scope.menuOptions.length).not.toBe(0); }); }); /***** Services *****/ describe('test NavList service', function () { var scope, NavList; beforeEach(inject(function ($rootScope, _NavList_) { scope = $rootScope.$new(); NavList = _NavList_; })); it('should expect menuOptions array to be returned when getMenuOptions function is called', function () { var stationId = 'Admin'; var menuOptions = NavList.getMenuOptions(stationId); expect(menuOptions.length).not.toBe(0); }); it('should expect admin menu option to be in menuOptions if stationId is Admin', function () { var stationId = 'Admin'; var menuOptions = NavList.getMenuOptions(stationId); var hasAdminOption = false; for (var i = 0; i < menuOptions.length; i++) { if (menuOptions[i].name.toUpperCase() == 'Admin'.toUpperCase()) { hasAdminOption = true; break; } } expect(hasAdminOption).toBe(true); }); it('should not expect admin menu option to be in menuOptions if stationId is not Admin', function () { var stationId = 'MasterPl'; var menuOptions = NavList.getMenuOptions(stationId); var hasAdminOption = false; for (var i = 0; i < menuOptions.length; i++) { if (menuOptions[i].name.toUpperCase() == 'Admin'.toUpperCase()) { hasAdminOption = true; break; } } expect(hasAdminOption).toBe(false); }); }); });