Unit testing AngularFire karma error - angularjs

I am getting this error when I run my karma unit script and I haven't been able to figure out why
Error: [$injector:unpr] Unknown provider: FBURLProvider <- FBURL
Here is my directive code
'use strict';
angular.module('userMenu', ['firebase'])
.directive('userMenu', function (FBURL, angularFire) {
return {
restrict: 'A',
scope: true ,
link: function postLink(scope, element, attrs) {
/**
* Returns the logged in user information
* #param {string} FBURL
* #param {object} scope
* #returns {promise}
*/
scope.getUserDataFromFirebase = function(FBURL, scope) {
var ref = new Firebase(FBURL + '/users/' + scope.auth.id);
return angularFire(ref, scope, 'user', {})
}
}
};
});
Here is my spec code
'use strict';
describe('Directive: userMenu', function () {
// load the directive's module
beforeEach(module('userMenu', 'firebase'));
var element,
elementScope,
scope;
beforeEach(inject(function ($rootScope, $compile, _FBURL_, _angularFire_) {
scope = $rootScope.$new();
element = angular.element('<div user-menu></div>');
element = $compile(element)(scope);
elementScope = element.scope();
}));
it('should get user data', inject(function ($compile) {
console.log(scope);
}));
});
To be honest I'm not that familiar with unit testing so I'm probably missing something really obvious but any help would be appreciated.

If everything is working in your app, but you're getting an error in your tests, then you need to add firebase to the Karma's files. Find your karma.conf.js (in yeoman generated ionic-angular add this to the Karma suite of Gruntfile.js), and have it resemble the following:
karma: {
options: {
...
files: [
...
'https://cdn.firebase.com/v0/firebase.js',
'app/bower_components/angular-fire/angularFire.js',
...
],
...
}
...
}
Then in your spec, include firebase:
beforeEach(module('Simplift', 'firebase'));
And every time you need to use the firebase service:
describe/it('some test desc ...', inject(function (..., $firebase) {
// now we can use $firebase!!
fireSync = $firebase(new Firebase('https://app_name.firebaseio.com'));
...
}));
Took me forever to figure this out, and hoping it will alleviate stress for someone. This works for me for now, but probably not the cleanest way to do it (please contribute suggestions!), since you're not actually stubbing out the firebase data, but you could add a 'test' url to your firebase DB.

The Firbase team had pointed me in the direction of some testing code in the Fireseed project that has subsequently been removed. Here is my unit test that includes the stubs that were in the Fireseed project
'use strict';
describe('Directive: userMenu', function () {
// load the directive's module
beforeEach(module('userMenu', 'firebase', function($provide) {
$provide.value('Firebase', firebaseStub());
$provide.value('FBURL', 'FAKE_FB_URL');
$provide.value('angularFireAuth', angularAuthStub());
}));
/**
* This is from https://github.com/firebase/angularFire-seed/blob/master/test/unit/servicesSpec.js
*/
function stub() {
var out = {};
angular.forEach(arguments, function(m) {
out[m] = jasmine.createSpy();
});
return out;
}
/**
* This is from https://github.com/firebase/angularFire-seed/blob/master/test/unit/servicesSpec.js
*/
function stub() {
var out = {};
angular.forEach(arguments, function(m) {
out[m] = jasmine.createSpy();
});
return out;
}
/**
* This is from https://github.com/firebase/angularFire-seed/blob/master/test/unit/servicesSpec.js
*/
function reject($q, error) {
var def = $q.defer();
def.reject(error);
return def.promise;
}
/**
* This is from https://github.com/firebase/angularFire-seed/blob/master/test/unit/servicesSpec.js
*/
function resolve($q, val) {
var def = $q.defer();
def.resolve(val);
return def.promise;
}
/**
* This is from https://github.com/firebase/angularFire-seed/blob/master/test/unit/servicesSpec.js
*/
function firebaseStub() {
// firebase is invoked using new Firebase, but we need a static ref
// to the functions before it is instantiated, so we cheat here by
// attaching the functions as Firebase.fns, and ignore new (we don't use `this` or `prototype`)
var fns = stub('set');
customSpy(fns, 'child', function() { return fns; });
var Firebase = function() {
angular.extend(this, fns);
return fns;
};
Firebase.fns = fns;
return Firebase;
}
/**
* This is from https://github.com/firebase/angularFire-seed/blob/master/test/unit/servicesSpec.js
*/
function angularAuthStub() {
var auth = stub('login', 'logout', 'createAccount', 'changePassword');
auth._authClient = stub('changePassword', 'createUser');
return auth;
}
/**
* This is from https://github.com/firebase/angularFire-seed/blob/master/test/unit/servicesSpec.js
*/
function customSpy(obj, m, fn) {
obj[m] = fn;
spyOn(obj, m).andCallThrough();
}
var element,
elementScope,
scope;
beforeEach(inject(function ($rootScope, $compile) {
scope = $rootScope.$new();
element = angular.element('<div user-menu></div>');
element = $compile(element)(scope);
elementScope = element.scope();
}));
it('should default to a login message', inject(function ($compile) {
scope.$digest();
var text = element.text();
expect(text).toBe('Please login');
}));
it('default message should contain a link to login page', inject(function ($compile) {
scope.$digest();
var href = element.find('a').attr('href');
expect(href).toBe('#/login');
}));
});

Related

Unit test controller with service in it

I'm trying to unit test a controller with a service injected into it. No matter what I seem to try, I get an error. Any assistance to help me get this going would be much appreciated. I'm using Angular/Karma/Jasmine to get run my tests.
There seem to be a lot of posts with similar stories but this feels like it may not be a duplicate - apologies if it is.
My controller looks like this:
(function() {
angular
.module('blah')
.controller('AdminController', AdminController);
/* #ngInject */
function AdminController($scope, toastr, adminService) {
activate();
/**
* Controller initialisation.
*/
function activate() {
getAllUsers();
}
/**
* Gets all users.
*/
function getAllUsers() {
adminService.getAllUsers()
.then(function(response) {
$scope.users = response.data;
})
.catch(function(error) {
toastr.error('Unable to load users', 'Error');
console.log(error);
});
}
}
})();
And my service looks like this:
(function() {
angular
.module('blah')
.factory('adminService', adminService);
/* #ngInject */
function adminService($http, environmentConfig) {
var service = {
getAllUsers: getAllUsers
};
return service;
/**
* Gets all user objects.
*/
function getAllUsers() {
return $http.get(environmentConfig.apiBaseUrl + '/user');
}
}
})();
and my unit tests look like this:
describe('AdminController', function() {
var ctrl,
adminService,
$scope;
var listOfTestUsers = [
{ name: 'Dave', id: 1 },
{ name: 'Bob', id: 2 },
{ name: 'Bill', id:3 }
];
beforeEach(function() {
module('blah');
});
beforeEach(inject(function($rootScope, $controller) {
adminService = {
getAllUsers: function() {}
};
spyOn(adminService, 'getAllUsers').and.returnValue(listOfTestUsers);
$scope = $rootScope.$new();
ctrl = $controller('AdminController', {
$scope: $scope,
adminService: adminService
});
}));
describe('The getAllUsers function should exist', function() {
it('should work', function() {
expect(ctrl).toBeDefined();
});
});
});
I get this error when running my Jasmine tests with Karma:
TypeError: adminService.getAllUsers(...).then is not a function
Here are a few things that I found wrong with the code.
.catch was used earlier. .then is called with two callbacks, a success callback and an error callback. So that's what I've done in your call to adminService.getAllUsers.
For the TypeError: adminService.getAllUsers(...).then is not a function that you were getting. You didn't mock getAllUsers properly. I've done that in the testCases file. It returns a function named then which was not available earlier.
Controller
(function() {
angular
.module('blah', [])
.controller('AdminController', AdminController);
/* #ngInject */
function AdminController($scope, toastr, adminService) {
$scope.greeting = "Hello World!";
/**
* Gets all users.
*/
$scope.getAllUsers = function() {
adminService.getAllUsers()
.then(function(response) {
$scope.users = response.data;
}, function(error) {
toastr.error('Unable to load users', 'Error');
console.log(error);
});
}
activate();
/**
* Controller initialisation.
*/
function activate() {
$scope.getAllUsers();
}
}
})();
environmentConfig Constant. Replace this with yours.
(function() {
angular.module('blah').constant('environmentConfig', {
apiBaseUrl: 'https://www.something.com'
})
})();
toastr Service. Replace this with yours
(function() {
angular
.module('blah')
.factory('toastr', toastr);
/* #ngInject */
function toastr() {
var service = {
error: error
};
return service;
/**
* Gets all user objects.
*/
function error(a, b) {
console.log("Here's the error : ", a);
}
}
})();
adminService
(function() {
angular
.module('blah')
.factory('adminService', adminService);
/* #ngInject */
function adminService($http, environmentConfig) {
/**
* Gets all user objects.
*/
function getAllUsers() {
return $http.get(environmentConfig.apiBaseUrl + '/user');
}
var service = {
getAllUsers: getAllUsers
};
return service;
}
})();
Test Cases
describe('controller: AdminController', function() {
var scope, $scope, toastr, adminService, AdminController, flag, $q;
flag = 'success';
var listOfTestUsers = [{
name: 'Dave',
id: 1
}, {
name: 'Bob',
id: 2
}, {
name: 'Bill',
id: 3
}];
beforeEach(module('blah'));
beforeEach(inject(function($controller, $rootScope, _toastr_, _adminService_, _$q_) {
scope = $rootScope.$new();
toastr = _toastr_;
adminService = _adminService_;
$q = _$q_;
spyOn(adminService, 'getAllUsers').and.callFake(function() {
return flag === 'success' ? $q.when(listOfTestUsers) : $q.reject("Error");
});
AdminController = $controller('AdminController', {
$scope: scope,
toastr: _toastr_,
adminService: _adminService_
});
}));
describe('The getAllUsers function should exist', function() {
it('should work', function() {
expect(AdminController).toBeDefined();
});
});
});
Hope this helps.
Your controller code is causing the error. You should be callling $scope.getAllUsers(); in your activate function, not "getAllUsers()".
Your actual error is the fact that your mock service does not have the function 'getAllUsers' here is a paste bin with the adjustments http://pastebin.com/LwG0CzUW
If you prefer you could adjust your test to call the actual service as the following pastebin.
http://pastebin.com/RSP4RfF9

Jasmine Testing angularjs controller

I am trying to test my controller but I get undefined when running karma start
This is my controller Code:
(function() {
'use strict';
angular
.module('tariffOptionsExtras')
.controller('tariffOptionsExtrasBookCtrl', tariffOptionsExtrasBookCtrl);
tariffOptionsExtrasBookCtrl.$inject = ['tariffOptionsExtrasSrv', '$location', 'cache', 'authSrv', '$stateParams',
'tariffOptionsExtrasBookSrv', 'minimumDuration', '$rootScope', 'userTypes', 'messageCtrl', '$state'];
function tariffOptionsExtrasBookCtrl(tariffOptionsExtrasSrv, $location, cache, authSrv, stateParams, tariffOptionsExtrasBookSrv, minimumDuration, $rootScope, userTypes, messageCtrl, $state) {
var vm = this;
var cacheName = 'tariffOptionsExtras';
var cacheCollection = cache.getCollection(cacheName);
vm.date = new Date();
vm.minimumDuration = minimumDuration;
//if statement commented out. prepaid users should be able to enter the flow.
Date(vm.bookOption.startDate)
}
if (!vm.bookOption) {
$state.go(pagesConfig.tariffOptionsExtras.name);
} else {
vm.infoLink = vm.bookOption.infoUrl;
}
/**
* Runs booking tarif extra post and in case of success the view is changed
*/
vm.book = function() {
//If bookoption not present, redirect to chooser
tariffOptionsExtrasBookSrv.bookTariffExtra(vm.bookOption).then(function(response) {
$rootScope.$broadcast('transactionFinished');
var item = response['salesOrderVBO'][0]['orderItems']['orderItem'][0];
if (item.product.action == 'BOOKED') {
vm.success = true;
}
}, function(reason) {
vm.errorMessage = reason.data.faultMessage;
});
};
vm.success = false;
vm.subscription = authSrv.getUserContract();
vm.msisdn = vm.subscription.subscription.msisdn;
}
})();
Unit Test with jasmine
describe('tariffOptionsExtras module', function() {
describe('tariffOptionsExtrasBook Controller', function() {
var tariffOptionsExtrasBookCtrl, tariffOptionsExtrasSrv, tariffOptionsExtrasBookSrv, authSrv;
// Step 1: Import the module this controller belongs to, and its dependencies
beforeEach(function() {
module('app.common');
module('tariffOptionsExtras');
});
// Step 2: Mock any service that initially used when the controller instantiate
// beforeEach(module(function($provide) {
// $provide.factory('tariffOptionsExtrasBookSrv', function() {
// var getSync;
// getBookedExtras = function() {
// return {
// then:function(){}
// };
// };
// getTariffBookableOptions = function() {
// return {
// then:function(){}
// };
// };
// return {
// getBookedExtras: getBookedExtras,
// getTariffBookableOptions:getTariffBookableOptions
// };
// });
// }));
beforeEach(module(function($provide) {
$provide.factory('authSrv', function() {
getUserContract = function() {
return {
subscription:{
msisdn:'491741660390',
mboName:'27434975'
},
contract:{
mboName:'27434975',
ban:'106491816',
}
};
};
return {
getUserContract: getUserContract,
};
});
}));
// Step 3: Inject $controller with its dependencies
beforeEach(function() {
// 1. Import the module
module('app');
// 2. Inject $controller
inject(function($controller, $rootScope, _authSrv_, _tariffOptionsExtrasSrv_, _tariffOptionsExtrasBookSrv_) {
authSrv = _authSrv_;
tariffOptionsExtrasSrv = _tariffOptionsExtrasSrv_;
tariffOptionsExtrasBookSrv = _tariffOptionsExtrasBookSrv_;
// 3. Use $controller to instantiate the controller
tariffOptionsExtrasBookCtrl = $controller('tariffOptionsExtrasBookCtrl', {
'authSrv': authSrv,
'tariffOptionsExtrasSrv': tariffOptionsExtrasSrv,
'tariffOptionsExtrasBookSrv': tariffOptionsExtrasBookSrv
});
});
});
// Step 4: Test the controller
it('Should return sum of 1+1', function() {
expect(1+1).toBe(2);
});
});
});
When running karma Start I get this screen:
enter image description here
Also when I comment this block of code it works:
tariffOptionsExtrasBookCtrl = $controller('tariffOptionsExtrasBookCtrl', {
'authSrv': authSrv,
'tariffOptionsExtrasSrv': tariffOptionsExtrasSrv,
'tariffOptionsExtrasBookSrv': tariffOptionsExtrasBookSrv
});
first you have to inject the $controller param must be enclosed with underscores
then assign it to the $controller variable as this example
beforeEach(inject(function ($rootScope, _$controller_) {
scope = $rootScope.$new();
$controller = _$controller_;
$controller('nameController', {$scope: scope});
}));

AngularJS Testing and $http

So Im trying to figure out how to write unit tests for my angular controller. I am using karma as my runner. I was able to write 1 successful test but every time I try to write another test it yells at me about unexpected calls and such.
Here is my controller im trying to test.
(function (angular) {
'use strict';
var ngModule = angular.module('myApp.dashboardCtrl', []);
ngModule.controller('dashboardCtrl', function ($scope, $http) {
//"Global Variables"
var vm = this;
vm.success = false;
vm.repos = [];
//"Global Functions"
vm.addRepository = addRepository;
vm.listRepos = listRepos;
//Anything that needs to be instantiated on page load goes in the init
function init() {
listRepos();
}
init();
// Add a repository
function addRepository(repoUrl) {
$http.post("/api/repo/" + encodeURIComponent(repoUrl)).then(function (){
vm.success = true;
vm.addedRepo = vm.repoUrl;
vm.repoUrl = '';
listRepos();
});
}
//Lists all repos
function listRepos() {
$http.get('/api/repo').then( function (response){
vm.repos = response.data;
});
}
});
}(window.angular));
So I have a test written for listRepos(). It goes as follows
describe('dashboardCtrl', function() {
var scope, httpBackend, createController;
// Set up the module
beforeEach(module('myApp'));
beforeEach(inject(function($rootScope, $httpBackend, $controller) {
httpBackend = $httpBackend;
scope = $rootScope.$new();
createController = function() {
return $controller('dashboardCtrl', {
'$scope': scope
});
};
}));
afterEach(function() {
httpBackend.verifyNoOutstandingExpectation();
httpBackend.verifyNoOutstandingRequest();
});
it('should call listRepos and return all repos from the database', function() {
var controller = createController();
var expectedResponse = [{id: 12345, url: "https://github.com/myuser/myrepo.git"}];
httpBackend.expect('GET', '/api/repo')
.respond(expectedResponse);
httpBackend.flush();
scope.$apply(function() {
scope.listRepos;
});
expect(controller.repos).toEqual(expectedResponse);
});
This works and the test passes. Now my problem is I want to write another test to test the other function that calls a new api endpoint.
This is the test im trying to write for addRepository.
it('should addRepository to the database', function() {
var controller = createController();
var givenURL = "https://github.com/myuser/myURLtoMyRepo.git";
httpBackend.expect('POST', '/api/repo/' + encodeURIComponent(givenURL)).respond('success');
httpBackend.flush();
scope.$apply(function() {
scope.addRepository(givenURL);
});
expect(controller.success).toBe(true);
expect(controller.listRepos).toHaveBeenCalled();
});
The error I get when I add this test to the spec is:
Error: Unexpected request: GET /api/repo
Expected POST /api/repo/https%3A%2F%2Fgithub.com%2Fmyuser%2FmyURLtoMyRepo.git
at $httpBackend
Error: [$rootScope:inprog] $digest already in progress
http://errors.angularjs.org/1.4.8/$rootScope/inprog?p0=%24digest
The example I am working with is this one here
Any suggestions or tips is greatly appreciated!
UPDATE:
So changed my function to return the promise from the $http.post,
I rewrote my 2nd test and also wrapped my first test in a describe block describing the function its trying to test.
With the following:
describe('addRepository', function () {
it('should addRepository to the database', function () {
var controller = createController();
var givenURL = "https://github.com/myuser/myURLtoMyRepo.git";
httpBackend.expect('POST', '/api/repo/' + encodeURIComponent(givenURL)).respond('success');
scope.$apply(function () {
scope.addRepository(givenURL);
});
httpBackend.flush();
expect(controller.success).toBe(true);
});
it('should call listRepos', function() {
var controller = createController();
httpBackend.expect('GET', '/api/repo').respond('success');
controller.controller().then(function (result) {
expect(controller.listRepos).toHaveBeenCalled();
});
httpBackend.flush();
});
});
I still get the error:
Error: Unexpected request: GET /api/repo
Expected POST /api/repo/https%3A%2F%2Fgithub.com%2Fmyuser%2FmyURLtoMyRepo.git
at $httpBackend
Error: [$rootScope:inprog] $digest already in progress
but also
TypeError: 'undefined' is not a function (evaluating 'controller.controller()')
Error: Unflushed requests: 1
which shows 2 tests failed.
The flush should come after the call to the function. I'd also change the function to return the promise from the $http.post:
// Add a repository
function addRepository(repoUrl) {
return $http.post("/api/repo/" + encodeURIComponent(repoUrl)).then(function (){
vm.success = true;
vm.addedRepo = vm.repoUrl;
vm.repoUrl = '';
listRepos();
});
}
And then in the test you can call it and test the success part:
EDIT
I changed the controller.controller() to what you have.
it('should call listRepos', function() {
// Your setup
ctrl.addRepository().then(function(result) {
expect(ctrl.listRepos).toHaveBeenCalled();
});
});
EDIT 2
I emulated as best i could your code and the tests I write for the code:
(function () {
'use strict';
angular
.module('myApp')
.controller('DashboardController',DashboardController);
DashboardController.$inject = ['$http'];
function DashboardController($http) {
var vm = this;
vm.success = false;
vm.repos = [];
vm.addRepository = addRepository;
vm.listRepos = listRepos;
init();
// Anything that needs to be instantiated on page load goes in the init
function init() {
vm.listRepos();
}
// Add a repository
function addRepository(repoUrl) {
return $http.post('http://jsonplaceholder.typicode.com/posts/1.json').then(function (){
vm.success = true;
vm.addedRepo = vm.repoUrl;
vm.repoUrl = '';
vm.listRepos();
});
}
// Lists all repos
function listRepos() {
return $http.get('http://jsonplaceholder.typicode.com/posts/1').then( function (response){
vm.repos = response.data;
});
}
};
}());
Here I'm using an online JSONPlaceholder API to simulate HTTP calls as I, obviously, can't hit what you're pointing at. And for the test (which all pass):
(function() {
'use strict';
fdescribe('DashBoardController', function() {
var $rootScope, scope, ctrl, $httpBackend;
beforeEach(module('myApp'));
beforeEach(inject(function(_$rootScope_, _$httpBackend_,$controller) {
$rootScope = _$rootScope_;
scope = $rootScope.$new();
$httpBackend =_$httpBackend_;
ctrl = $controller('DashBoardController',{
$scope: scope
});
}));
beforeEach(function() {
// Setup spies
spyOn(ctrl,'listRepos');
});
describe('controller', function() {
it('should be defined', function() {
expect(ctrl).toBeDefined();
});
it('should initialize variables', function() {
expect(ctrl.success).toBe(false);
expect(ctrl.repos.length).toBe(0);
});
});
describe('init', function() {
it('should call listRepos', function() {
$httpBackend.expectGET('http://jsonplaceholder.typicode.com/posts/1')
.respond({success: '202'});
$httpBackend.expectPOST('http://jsonplaceholder.typicode.com/posts/1.json')
.respond({success: '202'});
ctrl.addRepository().then(function(result) {
expect(ctrl.success).toBe(true);
expect(ctrl.repoUrl).toBe('');
expect(ctrl.listRepos).toHaveBeenCalled();
});
$httpBackend.flush();
});
});
});
}());

Angular Unit Test Jasmine Spy error

The following controller is getting a TypeError: 'undefined' is not a function (evaluating sessionService.getCurrentPlace()). I have a Mock Service with that method being spied on. The other method on the mock service works fine. I've tried .AndReturns({..}) on the spy as well as .AndCallThrough() but no luck. Any idea what I'm missing, or am I going about this wrong? Much Thanks!
CONTROLLER:
'use strict';
angular.module('wallyApp')
.controller('addGatewayCtrl', function ($scope, $location, $filter, sessionService) {
/*
private members
*/
//set scope from session data
$scope.processSession = function (places) {
$scope.currentPlaceId = sessionService.getCurrentPlace();
if (!$scope.currentPlaceId) {
$scope.currentPlaceId = places[0].id;
}
$scope.place = $filter("getById")(places, $scope.currentPlaceId);
$scope.ready = true;
};
/*
setup our scope
*/
$scope.currentPlaceId = null;
$scope.place = {};
$scope.videoSrc = "/videos/gateway-poster.gif";
$scope.loaded = true;
/*
setup controller behaivors
*/
//set video or gif to show or hide video
$scope.setVideo = function () {
$scope.videoSrc = "/videos/gateway.gif";
};
$scope.setPoster = function () {
$scope.videoSrc = "/videos/gateway-poster.gif";
};
//initialize scope
$scope.setVideo();
//submit form
$scope.continue = function () {
$location.path("/setup/pair-gateway");
return false;
};
//cancel
$scope.back = function () {
$location.path("/setup/plan-locations");
return false;
};
//wifi
$scope.gotoWifi = function () {
$location.path("/setup/wifi");
return false;
};
/*
setup our services, etc
*/
//get our places from the cache
sessionService.get("places").then(function (places) {
if (!places || places.length < 1) {
sessionService.refreshPlaces(); //Note we don't care about the promise as our broadcast watch will pick up when ready
} else {
$scope.processSession(places);
}
}).catch(function (error) {
//TODO:SSW Call Alert Service??
});
//Watch broadcast for changes
$scope.$on("draco.placesRefreshed", function (event, data) {
sessionService.get("places").then(function (places) {
$scope.processSession(places);
});
});
});
UNIT TEST:
'use strict';
describe('addGatewayCtrl', function () {
var $q,
$rootScope,
$location,
$scope,
$filter,
mockSessionService,
completePath = "/setup/pair-gateway",
backPath = "/setup/plan-locations",
wifiPath = "/setup/wifi",
sessionDeferred,
sessionInitDeferred,
mockPlaces = [{ id: "0001" }];
beforeEach(module('wallyApp'));
beforeEach(inject(function (_$q_, _$rootScope_, _$location_, _$filter_) {
$q = _$q_;
$location = _$location_;
$rootScope = _$rootScope_;
$filter = _$filter_;
}));
beforeEach(inject(function ($controller) {
$scope = $rootScope.$new();
mockSessionService = {
get: function (contact) {
sessionDeferred = $q.defer();
return sessionDeferred.promise;
},
getCurrentPlace: function () {
return mockPlaces[0].id;
},
refreshPlaces: function () {
sessionInitDeferred = $q.defer();
return sessionInitDeferred.promise;
}
};
spyOn(mockSessionService, 'get').andCallThrough();
spyOn(mockSessionService, 'getCurrentPlace').andReturn(mockPlaces[0].id);
spyOn(mockSessionService, 'refreshPlaces').andCallThrough();
$controller('addGatewayCtrl', {
'$scope': $scope,
'$location': $location,
'$filter':$filter,
'sessionService': mockSessionService
});
}));
describe('call session service to get place data ', function () {
//resolve our mock place and session services
beforeEach(function () {
//resolve mocks
sessionDeferred.resolve(mockPlaces);
$rootScope.$apply();
});
//run tests
it('should have called sessionService get places', function () {
expect(mockSessionService.get).toHaveBeenCalledWith("places");
});
it('should have called sessionService get currentPlaceId', function () {
expect(mockSessionService.getCurrentPlace).toHaveBeenCalled();
});
it('should have set scope', function () {
expect($scope.place).toEqual(mockPlaces[0]);
});
});
});
So I figured it out. With nested deferred's you have to call $scope.$apply() in between. The following fixed it up (along with a few minor changes to the mock data responses, but those were trivial):
//resolve promises
activityMessagesDeferred.resolve(mockActivityMessages);
$rootScope.$apply();
$rootScope.$broadcast("draco.sessionRefreshed");
activityCountDeferred.resolve(mockActivityCount);
$rootScope.$apply();
placesDeferred.resolve(mockPlaces);
activityListDeferred.resolve(mockActivities);
$rootScope.$apply();

I need to write a simple AngularJS unit test for a controller that passes as defined

Here is the controller and I just want to test that it is defined
'use strict';
mainApp.controller('HeaderCtrl',
function ($scope, sessionSrvc, eventSrvc, $state) {
// Initially keep both SingIn and SignOut as hidden
// until it's determined if the session is alive or not.
var user = sessionSrvc.getCurrentUser();
if(user.fullName != undefined) {
$scope.signInVisible = false;
$scope.signOutVisible = true;
} else {
user.then(function(user) {
if(user != null) {
$scope.user = user;
$scope.signInVisible = false;
$scope.signOutVisible = true;
} else {
$scope.signInVisible = true;
$scope.signOutVisible = false;
}
}, function(errorId) {
// alert(errorId);
});
}
/**
* This callback is called when the the user successfully logs in
* and the signIn dialog closes.
*/
$scope.$on(eventSrvc.getSignInSucceededEvent(), function() {
$scope.user = sessionSrvc.getCurrentUser();
$scope.signInVisible = false;
$scope.signOutVisible = true;
});
/**
* Show the SignIn dialog.
*/
$scope.signIn = function() {
$('#signIn').modal('show');
};
/**
* SignOut
*
* #param account
*/
$scope.signOut = function() {
var promise = sessionSrvc.signOut();
promise.then(function(user) {
$scope.signInVisible = true;
$scope.signOutVisible = false;
window.location.replace("/");
}, function(reason) {
// alert('Failed: ' + reason);
});
};
/**
* Detect if Cookies enabled
*/
checkCookie();
function checkCookie(){
var cookieEnabled=(navigator.cookieEnabled) ? true : false
if (typeof navigator.cookieEnabled=="undefined" && !cookieEnabled){
document.cookie="testcookie";
cookieEnabled=(document.cookie.indexOf("testcookie")!=-1)? true : false;
}
return (cookieEnabled) ? true:showCookieFail();
}
function showCookieFail(){
// alert('Please enable cookies, the site will not work correctly without them');
}
/**
* Set header back to portal header (from cookbook) if any tab is clicked
*/
$('body').on('click', '.nav li a', function(){
$('.header').removeClass('cookbook');
});
/**
* links to cookbook or other docs
*
* If user clicks back button or clicks any tab, changes header back to
* service portal (from cookbook).
*/
$scope.goHome = function() {
$('.header').removeClass('cookbook');
$state.go('home.explore');
};
window.onpopstate = function() {
$('.header').removeClass('cookbook');
};
}
);
Here is the karma test I set up that passes :) Please bear with me as unit testing in Angular is new to me. How can I properly define in my test
'use strict'
describe('HeaderCtrl', function(){
var scope, sessionSrvc, eventSrvc, state;
beforeEach(module('mainApp'));
it('should have a HeaderCtrl controller', function() {
expect(mainApp.HeaderCtrl).toBe(undefined);
});
});
Controllers are instantiated on demand, when a view or a route needs one. So, your app doesn't have any instance of your controller, and your controller is not a singleton like services are. So your test doesn't make sense as is. What you can test though is the ability to create an instance of the controller:
describe('HeaderCtrl', function(){
var $controller, $scope;
beforeEach(module('mainApp'));
beforeEach(inject(function($rootScope, _$controller_) {
$scope = $rootScope.$new();
$controller = _$controller_;
}));
it('should create a HeaderCtrl controller', function() {
var controller = $controller('HeaderCtrl', {
$scope: $scope
});
expect(controller).toBeDefined();
});
});

Resources