I am a front-end developer at Cirqle.nl
I am a member of CodeCatz
I tweet at @SvetkaSapelova
I get emails to svetsapelova@gmail.com
I come from Saint-Petersburg
I live in Ljubljana
..because we want to be sure our code works
..and because unit testing in Angularjs is pure fun
Oh hey, these are some notes. They'll be hidden in your presentation, but you can see them if you open the speaker notes window (hit 's' on your keyboard).Test the logic behind the components
Unit tests are all about mocks, stubs, spies, fakes, isolating …you name it
Angularjs is all about dependency injection
while(Dependency Injection) {
Angularjs.easyToMock = true;
}
...is a way to wire components of the app together
...enables loose coupling between the components
...is handled by Angularjs $injector service
Unit tests rely on the $injector to get all the components and ngMock module to mock angularjs services
'use strict';
angular.module('jsmeetupApp')
.controller('MainCtrl', function ($scope, DataService) {
//Chain the promise
DataService
.getData()
.then(function(response){
$scope.items = response.data;
},
function(err){
$scope.loadDataFailed = true;
});
});
beforeEach(inject(function ($controller, $rootScope, $q) {
scope = $rootScope.$new();
//create mock of the service
MockDataService = {
getData: function(){
defer = $q.defer();
return defer.promise
}
};
//initialize controller with mocked service
MainCtrl = $controller('MainCtrl', {
$scope: scope,
DataService: MockDataService
});
});
it('should attach a list of data to the scope', function () {
//resolve the promise
defer.resolve({data:['AngularJS', 'Emberjs', 'Backbone']});
//kick the angular digest loop to propagate promise resolution
scope.$apply();
expect(scope.items.length).toEqual(3);
});
it('should set loadDataFailed to true', function () {
//reject the promise
defer.reject();
scope.$apply();
expect(scope.loadDataFailed).toBeTruthy();
});
Don’t overload controllers with logic.
Slim controllers = testable controllers
It's possible to override Angularjs components by creating the new ones with the same name
beforeEach(module('jsmeetupApp', function($provide){
//this will override the original service
$provide.service('DataService',function() {
this.getData = function(){
defer = $q.defer();
return defer.promise
};
})
}));
beforeEach(inject(function ($controller, $rootScope, _$q_)
{
scope = $rootScope.$new();
$q = _$q_;
MainCtrl = $controller('MainCtrl', {$scope: scope});
}));
'use strict';
angular.module('jsmeetupApp')
.service('DataService', function($http){
this.getData = function(callback) {
$http.get('api/getdata')
.success(function(data){
callback(null,data)
})
.error(function(err){
callback(err,null)
})
};
});
/*in beforeEach inject $httpBackend */
/* and $injector */
$httpBackend.expectGET('api/getdata')
.respond(200,['Angular']);
DataService = $injector.get('DataService');
callback = jasmine.createSpy('callback');
....
it('should call the callback', function(){
DataService.getData(callback);
//return trained response
$httpBackend.flush();
expect(callback)
.toHaveBeenCalledWith(null,['Angular']);
});
they are reusable
angular.module('Dirs')
.directive('clicked', function(){
return {
link: function(scope,elem, attrs){
elem.bind('click', function(){
scope.clicked = true;
scope.$apply()
elem.hide();
});
},
template: '<button>Click Me</button>'
}
});
<div clicked></div>
make use of Angularjs $compile service
beforeEach(inject(function(_$compile_,$rootScope){
$compile = _$compile_;
scope = $rootScope.$new();
//that is how Angularjs turns html into view
element = $compile('<div clicked></div>')(scope);
scope.$digest();
}));
$compile => $link => $digest
it('should display the button', function(){
//verify the template has been loaded
expect(element.find('button').length).toEqual(1);
});
it('should add display:none style', function(){
element.triggerHandler('click');
//we can chech the attrs of the element
expect(element.css('display')).toEqual('none');
//we can get access to the scope of the element
expect(element.scope().clicked).toBeTruthy();
});
Testem (test runner ) github.com/airportyh/testem Mocha (javascript test framework) github.com/mochajs/mocha QUnit (javascript test framework) qunitjs.com * zachlendon.github.io/blog/2013/03/26/quick-thoughts-on-testem-vs-testacular-karma techtalkdc.com/which-javascript-test-library-should-you-use-qunit-vs-jasmine-vs-mocha
ng-book.comng-newsletterdocs.angularjs.org/guide/unit-testinglinkis.com/smashingmagazine.com/kG2hs yearofmoo.com/2013/01/full-spectrum-testing-with-angularjs-and-karma.htmls