Tyson Gern @tygern
According to Kent Beck...
Don't write a line of new code unless you first have a failing automated test. Eliminate duplication.Write a short failing test
Make the test pass in the simplest way possible
Eliminate duplication introduced in the previous step
Documents behavior of code
// adds one to the passed in value function increment(number) { return number + 2; }
Leads to simple design
Allows frequent refactoring
Quickly add features
javascript testing framework
Function addStrings that adds integers passed as strings.
describe('addStrings', function () { });
describe('addStrings', function () { it('correctly adds two numbers', function () { }); });
describe('addStrings', function () { it('correctly adds two numbers', function () { expect(addStrings('3', '5')).toEqual(8); }); });
describe('addStrings', function () { it('correctly adds two numbers', function () { expect(addStrings('3', '5')).toEqual(8); expect(addStrings('4', '6')).toEqual(10); }); });
describe('addStrings', function () { it('correctly adds two numbers', function () { expect(addStrings('3', '5')).toEqual(8); expect(addStrings('4', '6')).toEqual(10); }); });ReferenceError: addStrings is not defined
describe('addStrings', function () { it('correctly adds two numbers', function () { expect(addStrings('3', '5')).toEqual(8); expect(addStrings('4', '6')).toEqual(10); }); });
function addStrings() { }
describe('addStrings', function () { it('correctly adds two numbers', function () { expect(addStrings('3', '5')).toEqual(8); expect(addStrings('4', '6')).toEqual(10); }); });
function addStrings() { }Expected undefined to equal 8.
describe('addStrings', function () { it('correctly adds two numbers', function () { expect(addStrings('3', '5')).toEqual(8); expect(addStrings('4', '6')).toEqual(10); }); });
function addStrings() { return 8; }
describe('addStrings', function () { it('correctly adds two numbers', function () { expect(addStrings('3', '5')).toEqual(8); expect(addStrings('4', '6')).toEqual(10); }); });
function addStrings() { return 8; }Expected 8 to equal 10.
describe('addStrings', function () { it('correctly adds two numbers', function () { expect(addStrings('3', '5')).toEqual(8); expect(addStrings('4', '6')).toEqual(10); }); });
function addStrings(first, second) { return parseInt(first) + parseInt(second); }
describe('addStrings', function () { it('correctly adds two numbers', function () { expect(addStrings('3', '5')).toEqual(8); expect(addStrings('4', '6')).toEqual(10); }); });
function addStrings(first, second) { return parseInt(first) + parseInt(second); }
Handle bad data. Count non-numbers as 0.
describe('addStrings', function () { //... it('treats non-numbers as 0', function () { }); });
function addStrings(first, second) { return parseInt(first) + parseInt(second); }
describe('addStrings', function () { //... it('treats non-numbers as 0', function () { expect(addStrings('a', '6')).toEqual(6); }); });
function addStrings(first, second) { return parseInt(first) + parseInt(second); }
describe('addStrings', function () { //... it('treats non-numbers as 0', function () { expect(addStrings('a', '6')).toEqual(6); expect(addStrings('2', '')).toEqual(2); }); });
function addStrings(first, second) { return parseInt(first) + parseInt(second); }
describe('addStrings', function () { //... it('treats non-numbers as 0', function () { expect(addStrings('a', '6')).toEqual(6); expect(addStrings('2', '')).toEqual(2); }); });
function addStrings(first, second) { return parseInt(first) + parseInt(second); }Expected NaN to equal 6.
describe('addStrings', function () { //... it('treats non-numbers as 0', function () { expect(addStrings('a', '6')).toEqual(6); expect(addStrings('2', '')).toEqual(2); }); });
function addStrings(first, second) { var firstNumber = parseInt(first); if(isNaN(firstNumber)) { firstNumber = 0; } return firstNumber + parseInt(second); }
describe('addStrings', function () { //... it('treats non-numbers as 0', function () { expect(addStrings('a', '6')).toEqual(6); expect(addStrings('2', '')).toEqual(2); }); });
function addStrings(first, second) { var firstNumber = parseInt(first); if(isNaN(firstNumber)) { firstNumber = 0; } return firstNumber + parseInt(second); }Expected NaN to equal 2.
describe('addStrings', function () { //... it('treats non-numbers as 0', function () { expect(addStrings('a', '6')).toEqual(6); expect(addStrings('2', '')).toEqual(2); }); });
function addStrings(first, second) { var firstNumber = parseInt(first); if(isNaN(firstNumber)) { firstNumber = 0; } var secondNumber = parseInt(second); if(isNaN(secondNumber)) { secondNumber = 0; } return firstNumber + secondNumber; }
describe('addStrings', function () { //... it('treats non-numbers as 0', function () { expect(addStrings('a', '6')).toEqual(6); expect(addStrings('2', '')).toEqual(2); }); });
function addStrings(first, second) { var firstNumber = parseInt(first); if(isNaN(firstNumber)) { firstNumber = 0; } var secondNumber = parseInt(second); if(isNaN(secondNumber)) { secondNumber = 0; } return firstNumber + secondNumber; }
describe('addStrings', function () { //... it('treats non-numbers as 0', function () { expect(addStrings('a', '6')).toEqual(6); expect(addStrings('2', '')).toEqual(2); }); });
function addStrings(first, second) { var firstNumber = parseInt(first); if(isNaN(firstNumber)) { firstNumber = 0; } var secondNumber = parseInt(second); if(isNaN(secondNumber)) { secondNumber = 0; } return firstNumber + secondNumber; }
describe('addStrings', function () { //... it('treats non-numbers as 0', function () { expect(addStrings('a', '6')).toEqual(6); expect(addStrings('2', '')).toEqual(2); }); });
function addStrings(first, second) { function parseNumber(string) { var number = parseInt(string); return isNaN(number) ? 0 : number; } return parseNumber(first) + parseNumber(second); }
describe('addStrings', function () { //... it('treats non-numbers as 0', function () { expect(addStrings('a', '6')).toEqual(6); expect(addStrings('2', '')).toEqual(2); }); });
function addStrings(first, second) { function parseNumber(string) { var number = parseInt(string); return isNaN(number) ? 0 : number; } return parseNumber(first) + parseNumber(second); }
javascript web framework
Angular synchronizes controllers and views with $scope.
<div ng-controller="messaging.flashController"> <h3>{{message}}</h3> </div>
+
angular.module('messaging') .controller('messaging.flashController', function ($scope) { $scope.message = 'hello'; });
=
<div ng-controller="messaging.flashController"> <h3>hello</h3> </div>
Angular uses dependency injection to provide objects with collaborators.
angular.module('messaging') .controller('messaging.flashController', function($scope, messagingService) { });
Modules allow building application with modules.
angular.module('users', []); // greeterApplication angular.module('twitterAdapter', []); // / \ angular.module('messaging', [ // / \ 'twitterAdapter' // / \ ]); // messaging users // | angular.module('greeterApplication', [ // | 'users', // | 'messaging' // twitterAdapter ]); //
Start to build greeterApplication
Show initial greeting when the page loads
<div ng-app="greeterApplication"> <div ng-controller="messaging.flashController"> <h3>{{message}}</h3> </div> </div>
describe('messaging.flashController', function () { beforeEach(module('messaging')); // tests go here });
describe('messaging.flashController', function () { var $scope; beforeEach(module('messaging')); beforeEach(inject(function ($rootScope) { $scope = $rootScope.$new(); })); // tests go here });
describe('messaging.flashController', function () { var $scope; beforeEach(module('messaging')); beforeEach(inject(function ($rootScope, $controller) { $scope = $rootScope.$new(); $controller('messaging.flashController', { $scope: $scope }); })); // tests go here });
describe('messaging.flashController', function () { var $scope, messagingService; beforeEach(module('messaging')); beforeEach(inject(function (..., _messagingService_) { $scope = $rootScope.$new(); messagingService = _messagingService_; spyOn(messagingService, 'getMessage').and.returnValue('Hi!'); $controller('messaging.flashController', { $scope: $scope, messagingService: messagingService }); })); // tests go here });
describe('messaging.flashController', function () { // setup describe('when the controller loads', function () { it('sets the message', function () { }); }); });
describe('messaging.flashController', function () { // setup describe('when the controller loads', function () { it('sets the message', function () { expect($scope.message).toEqual('Hi!'); }); }); });
describe('messaging.flashController', function () { // setup describe('when the controller loads', function () { it('sets the message', function () { expect($scope.message).toEqual('Hi!'); expect(messagingService.getMessage) .toHaveBeenCalledWith('initial'); }); }); });
describe('messaging.flashController', function () { // setup describe('when the controller loads', function () { it('sets the message', function () { expect($scope.message).toEqual('Hi!'); expect(messagingService.getMessage) .toHaveBeenCalledWith('initial'); }); }); });Expected undefined to equal 'Hi!'.
describe('messaging.flashController', function () { // setup describe('when the controller loads', function () { it('sets the message', function () { expect($scope.message).toEqual('Hi!'); expect(messagingService.getMessage) .toHaveBeenCalledWith('initial'); }); }); });Expected spy getMessage to have been called with [ 'initial' ] but it was never called.
angular.module('messaging') .controller('messaging.flashController', function($scope, messagingService) { });
angular.module('messaging') .controller('messaging.flashController', function($scope, messagingService) { $scope.message = messagingService.getMessage('initial'); });
angular.module('messaging') .controller('messaging.flashController', function($scope, messagingService) { $scope.message = messagingService.getMessage('initial'); });
angular.module('messaging') .controller('messaging.flashController', function($scope, messagingService) { $scope.message = messagingService.getMessage('initial'); });
angular.module('messaging') .controller('messaging.flashController', function($scope, messagingService) { $scope.message = messagingService.getMessage('initial'); });
Change the message to prompt the user after 5 seconds
<div ng-app="greeterApplication"> <div ng-controller="messaging.flashController"> <h3>{{message}}</h3> </div> </div>
describe('messaging.flashController', function () { // tests and setup describe('after waiting 5 seconds', function () { it('shows the \'prompt\' message', function () { }); }); });
describe('messaging.flashController', function () { // tests and setup describe('after waiting 5 seconds', function () { it('shows the \'prompt\' message', function () { $scope.message = ''; $timeout.flush(4900); expect($scope.message).toEqual(''); }); }); });
describe('messaging.flashController', function () { // tests and setup describe('after waiting 5 seconds', function () { it('shows the \'prompt\' message', function () { $scope.message = ''; $timeout.flush(4900); expect($scope.message).toEqual(''); $timeout.flush(200); expect($scope.message).toEqual('Hi!'); expect(messagingService.getMessage) .toHaveBeenCalledWith('prompt'); }); }); });
describe('messaging.flashController', function () { // tests and setup describe('after waiting 5 seconds', function () { it('shows the \'prompt\' message', function () { $scope.message = ''; $timeout.flush(4900); expect($scope.message).toEqual(''); $timeout.flush(200); expect($scope.message).toEqual('Hi!'); expect(messagingService.getMessage) .toHaveBeenCalledWith('prompt'); }); }); });Expected '' to equal 'Hi!'.
describe('messaging.flashController', function () { // tests and setup describe('after waiting 5 seconds', function () { it('shows the \'prompt\' message', function () { $scope.message = ''; $timeout.flush(4900); expect($scope.message).toEqual(''); $timeout.flush(200); expect($scope.message).toEqual('Hi!'); expect(messagingService.getMessage) .toHaveBeenCalledWith('prompt'); }); }); });Expected spy getMessage to have been...
angular.module('messaging') .controller('messaging.flashController', function($scope, $timeout, messagingService) { $scope.message = messagingService.getMessage('initial'); });
angular.module('messaging') .controller('messaging.flashController', function($scope, $timeout, messagingService) { $scope.message = messagingService.getMessage('initial'); $timeout(function () { $scope.message = messagingService.getMessage('prompt'); }, 5000); });
angular.module('messaging') .controller('messaging.flashController', function($scope, $timeout, messagingService) { $scope.message = messagingService.getMessage('initial'); $timeout(function () { $scope.message = messagingService.getMessage('prompt'); }, 5000); });
angular.module('messaging') .controller('messaging.flashController', function($scope, $timeout, messagingService) { $scope.message = messagingService.getMessage('initial'); $timeout(function () { $scope.message = messagingService.getMessage('prompt'); }, 5000); });
angular.module('messaging') .controller('messaging.flashController', function($scope, $timeout, messagingService) { function setMessage(type) { $scope.message = messagingService.getMessage(type); } setMessage('initial'); $timeout(function () { setMessage('prompt'); }, 5000); });
angular.module('messaging') .controller('messaging.flashController', function($scope, $timeout, messagingService) { function setMessage(type) { $scope.message = messagingService.getMessage(type); } setMessage('initial'); $timeout(function () { setMessage('prompt'); }, 5000); });
Show the current user
<div ng-app="greeterApplication"> <div ng-controller="users.currentController"> <h2>Welcome {{currentUser}}!</h2> </div> <div ng-controller="messaging.flashController"> <h3>{{message}}</h3> </div> </div>
describe('users.currentController', function () { var $scope, usersService, deferred; beforeEach(module('users')); beforeEach(inject(function ($rootScope, $controller, $q, _usersService_) { $scope = $rootScope.$new(); usersService = _usersService_; $controller('users.currentController', { $scope: $scope, usersService: usersService }); })); // tests go here });
describe('users.currentController', function () { var $scope, usersService, deferred; beforeEach(module('users')); beforeEach(inject(function ($rootScope, $controller, $q, _usersService_) { $scope = $rootScope.$new(); usersService = _usersService_; deferred = $q.defer(); $controller('users.currentController', { $scope: $scope, usersService: usersService }); })); // tests go here });
describe('users.currentController', function () { var $scope, usersService, deferred; beforeEach(module('users')); beforeEach(inject(function ($rootScope, $controller, $q, _usersService_) { $scope = $rootScope.$new(); usersService = _usersService_; deferred = $q.defer(); spyOn(usersService, 'getCurrent') .and.returnValue(deferred.promise); $controller('users.currentController', { $scope: $scope, usersService: usersService }); })); // tests go here });
describe('when the controller loads', function () { it('sets the current user', function () { }); });
describe('when the controller loads', function () { it('sets the current user', function () { expect($scope.currentUser).toEqual('Loading'); }); });
describe('when the controller loads', function () { it('sets the current user', function () { expect($scope.currentUser).toEqual('Loading'); deferred.resolve('@walken20'); }); });
describe('when the controller loads', function () { it('sets the current user', function () { expect($scope.currentUser).toEqual('Loading'); deferred.resolve('@walken20'); $scope.$apply(); }); });
describe('when the controller loads', function () { it('sets the current user', function () { expect($scope.currentUser).toEqual('Loading'); deferred.resolve('@walken20'); $scope.$apply(); expect($scope.currentUser).toEqual('@walken20'); }); });
describe('when the controller loads', function () { it('sets the current user', function () { expect($scope.currentUser).toEqual('Loading'); deferred.resolve('@walken20'); $scope.$apply(); expect($scope.currentUser).toEqual('@walken20'); }); });Expected undefined to equal 'Loading'.
describe('when the controller loads', function () { it('sets the current user', function () { expect($scope.currentUser).toEqual('Loading'); deferred.resolve('@walken20'); $scope.$apply(); expect($scope.currentUser).toEqual('@walken20'); }); });Expected undefined to equal '@walken20'.
angular.module('users') .controller('users.currentController', function($scope, usersService) { });
angular.module('users') .controller('users.currentController', function($scope, usersService) { $scope.currentUser = 'Loading'; });
angular.module('users') .controller('users.currentController', function($scope, usersService) { $scope.currentUser = 'Loading'; usersService.getCurrent().then(function (result) { $scope.currentUser = result; }); });
angular.module('users') .controller('users.currentController', function($scope, usersService) { $scope.currentUser = 'Loading'; usersService.getCurrent().then(function (result) { $scope.currentUser = result; }); });
angular.module('users') .controller('users.currentController', function($scope, usersService) { $scope.currentUser = 'Loading'; usersService.getCurrent().then(function (result) { $scope.currentUser = result; }); });
angular.module('users') .controller('users.currentController', function($scope, usersService) { function setCurrentUser(user) { $scope.currentUser = user; } setCurrentUser('Loading'); usersService.getCurrent().then(setCurrentUser); });
angular.module('users') .controller('users.currentController', function($scope, usersService) { function setCurrentUser(user) { $scope.currentUser = user; } setCurrentUser('Loading'); usersService.getCurrent().then(setCurrentUser); });