I'm starting to do unit testing for my Angular app, and I had some questions on how to actually structure the tests folder. I basically used the yeoman angular generator, so it comes with Jasmine and Karma pre-configured.
Here's a scenario of what I'm trying to test...
I have a "PageHeaderDirective" which displays a user's name and email (like a welcome message) as well as a logout link. The code for the page header directive is inconsequential, but I do need to hit the "/user" endpoint from the backend to get the user's details. Here is the code for UserService which is injected into PageHeaderDirective:
/**
* @ngdoc function
* @name Common.service.UserService
* @description
* Service to retrieve a {@link User} from the backend.
*/
(function () {
'use strict';
angular.module('Common').service('UserService', UserService);
UserService.$inject = ['User', 'Restangular'];
/**
* User service function.
* @param User The {@link User} provider.
* @param Restangular The restangular provider.
*/
function UserService(User, Restangular) {
var userPromise;
return {
getUser: getUser
};
/**
* Retrieves a {@link User} instance from the /user endpoint.
* @returns A promise to be resolved with a {@link User} instance.
*/
function getUser() {
if(!userPromise) {
userPromise = Restangular.one('user').get().then(function(data) {
return User.factory(data);
});
}
return userPromise;
}
}
})();
Here is a really simple test for PageHeaderDirective:
describe('Pageheader Tests', function() {
'use strict';
var scope;
var element;
beforeEach(module('templates'));
beforeEach(module('Common'));
beforeEach(inject(function(_$rootScope_, $compile) {
scope = _$rootScope_.$new();
scope.message = 'Test message';
element = '<ft-page-header message="message" page="home"></ft-page-header>';
element = $compile(element)(scope);
scope.$digest();
}));
it('should render a page header with the logo and username', function() {
expect(element.find('.logo-text').length).toBe(1);
var isolateScope = element.isolateScope();
expect(isolateScope.name).toBe('test');
});
});
Now, as you can probably tell, I'm getting an unknown provider error "Unknown provider: RestangularProvider <- Restangular <- UserService <- pageHeaderDirective" because I haven't injected it into the tests.
I've read that you can do something like beforeEach(function(){ module(function($provide) { $provide.service('UserService', function() { ... }})}); in each test file, but I don't really want to do that any time a directive/controller uses the UserService. How do I break that portion out of each test file and put it into its own "UserService.mock.js" file? If it's possible, how would I inject the "UserService.mock.js" into my tests?
Secondly, I'm also injecting Restangular into PageHeaderDirective to logout the user (Restangular.one('logout').get().then...). How do I mock this (I don't ever want to call the API endpoints)?
Lastly, if there are other providers that I am injecting ($document, $localStorage, $window), do I need to inject all of these into the tests as well? If so, how?
Thanks!