Last year I was involved in making a prototype web app using AngularJS, and as it there was the possibility that it may end up as the foundation to production code we wrote unit tests for it’s internal functionality. Whilst there is a ton of examples for testing AngularJS apps we still ran into a few issues so I’m going to try to expand on some bits of setup.
Karma config and fixtures
If you want to load any fixtures Karma must be aware of the files though it’s config;
files: [ { pattern: 'test/fixtures/**/*.*', watched: true, included: false, served: true } ]
This allows you to import these through Jasmine;
// given relative path test/fixtures/ to karma var path = ''; if (typeof window.__karma__ !== 'undefined') { path += 'base/'; } jasmine.getFixtures().fixturesPath = path + 'test/fixtures'; jasmine.getJSONFixtures().fixturesPath = path + 'test/fixtures';
Change the response of a mocked service
When mocking your services it’s common to directly call ‘respond’ after whenGet (or similar method). If instead you hold onto the result of the whenGet call you can change it’s response at a later stage, allowing you to easily test your fail conditions.
beforeEach(inject(function($injector) { // Set up the mock http service responses $httpBackend = $injector.get('$httpBackend'); var responseMock = window.getJSONFixture('service-export.json'); submissionResponder = $httpBackend.whenGET(/^\/api\/export*/); submissionResponder .respond(responseMock); }));
Mock AngularJS cookies
Whilst the code itself is straight forward it’s important to note that the ngCookies mock does not set real cookies.
beforeEach(angular.mock.module('ngCookies', 'yourApp')); // Set an activeUser cookie beforeEach(inject(function ($cookieStore) { $cookieStore.put('activeUser', { id: 1 }); }));
Mock Google Maps (or any other external library)
beforeEach(module('yourApp', function ($provide) { var mockMapApi = { get : function() { return { then : function (callback) { //apply the given callback to a variable in the current scope so we can test it's result? //suppliedCallback = callback; } }; } }; $provide.value('mapApi', mockMapApi); })); //mock the Google Map service beforeEach(inject(function ($window) { var mockGoogle = { maps : { Map : function () {}, Marker : function (markerParam) { markerValue = markerParam; }, Animation : { DROP : '' } } }; $window.google = mockGoogle; }));
Test a nested Controller
beforeEach(inject(function ($rootScope, $controller) { //setup the parent scope topScope = $rootScope.$new(); $controller('AppCtrl', { $scope: topScope }); //create controller scope scope = topScope.$new(); thisCtrl = $controller('InnerCtrl', { $scope: scope }); }));
Mock a parent Controller or Directive
Rather than trying to test a nested directive by compiling the full HTML it’s simpler to mock the parent directives
beforeEach(inject(function ($rootScope, $compile) { var elementHTML = jasmine.getFixtures().read('source.html'); parentScope = $rootScope.$new(); //fake the parent directive parentScope.payments = { fromaccount : fromAccountId, toaccount : '' }; parentScope.isTransfer = false; scope = parentScope.$new(); element = angular.element(elementHTML); element = $compile(element)(scope); }));