It’s in the docs here, but not explained terribly well. I think most of the angular internals, except for $injector
for some reason, just get wrapped in underscores in tests.
The $provider
vs $service
vs $factory
gets into a part of angular that I don’t really understand and don’t really enjoy. IMO it’s starts to feel like stuff that only the framework developers should care about and not stuff that people using ng to build apps should have to worry about.
My understanding of rootScope vs Scope is that when you create an ng-app
it creates its own $rootScope
that all the other $scopes
inherit from. When you’re inside a controller, you are working with that controller’s $scope
, but you can still $broadcast
events on the $rootScope
if you need to communicate something across the application (although this has turned into a bad practice and should probably be done with a factory
instead).
When you’re testing, especially with controllers and directives, it’s very common to have to call $rootScope.$digest()
to trigger angulars digest cycle and update any scopes that may have changed.
I did this yesterday:
in my controller there’s a function that gets called when the controller is created to pull some data from local storage:
cacheService.getOne('userCache', $stateParams.userId).then(function(resp){
$scope.user = resp;
});
in order to test the value of $scope.user
angular needs to know it changed, so there needs to be a digest cycle:
describe('myCtrl', function(){
var fakeParams;
beforeEach(function(){
fakeParams = {userId: 99}
module('myapp');
module(function($provide){
$provide.factory('$stateParams', function(){
return fakeParams;
});
});
inject(function(_$rootScope_, _$controller_, _$httpBackend_, _$q_,
_cacheService_){
$rootScope = _$rootScope_;
$httpBackend = _$httpBackend_;
$q = _$q_;
// mock all template calls
$httpBackend.when('GET', /\.html$/).respond('..')
$scope = {};
cacheService = _cacheService_;
userCache = {
userId: 99,
name: 'super man'
}
// mock this before instantiating controller since it's called on init
spyOn(cacheService, 'getOne').and.callFake(function(){
var defer = $q.defer();
defer.resolve(userCache);
return defer.promise;
});
controller = _$controller_('myCtrl', {$scope: $scope});
});
});
describe('$scope.headerTitle', function(){
it('provides a title', function(){
expect($scope.headerTitle).toEqual('Confirm');
});
});
describe('$scope.user', function(){
it('loads the correct user from cache', function(){
// force a digest so $scope.patient gets updated, otherwise returns undefined.
$rootScope.$digest();
expect(cacheService.getOne).toHaveBeenCalledWith('userCache', 99)
expect($scope.patient.name).toEqual('super man');
});
});
});
It’s too much code for me to test in angular, I’m basically just killing time waiting for ember 2.0 this summer haha. Hope that helps!