An Introduction To Unit Testing In AngularJS Applications
- By Sébastien Fragnaud
- October 7th, 2014
- 0 Comments
AngularJS1 has grown to turn one of a many renouned single-page concentration frameworks. Developed by a dedicated group during Google, a outcome is estimable and widely used in both village and attention projects.
One of a reasons for AngularJS’ success is a superb ability to be tested. It’s strongly upheld by Karma112 (the fantastic exam curtain created by Vojta Jína) and a churned plugins. Karma, total with a fellows Mocha173, Chai184 and Sinon205, offers a finish toolset to furnish peculiarity formula that is easy to maintain, bug-free and good documented.
“Well, I’ll customarily launch a app and see if all works. We’ve never had any problem doing that – No one ever.”
The categorical cause that done me switch from “Well, we customarily launch a app and see if all works” to “I’ve got territory tests!” was that, for a initial time, we could focus on what matters and on what we suffer in programming: formulating intelligent algorithms and good UIs.
I remember a member that was ostensible to conduct a right-click menu in an application. Trust me, it was a formidable component. Depending on dozens of churned conditions, it could uncover or censor buttons, submenus, etc. One day, we updated a concentration in production. we can remember how we felt when we launched a app, non-stop something, right-clicked and saw no contextual menu — customarily an dull nauseous box that was decisive explanation that something had left unequivocally wrong. After carrying bound it, re-updated a concentration and apologized to patron service, we motionless to wholly rewrite this member in test-driven growth style. The exam record finished adult being twice as prolonged as a member file. It has been softened a lot since, generally a bad performance, yet it never unsuccessful again in production. Rock-solid code.
A Word About Unit Testing
Unit contrast has turn a customary in many program companies. Customer expectations have reached a new high, and no one accepts removing dual giveaway regressions for a cost of one refurbish anymore.
If we are informed with territory testing, afterwards you’ll already know how assured a developer feels when refactoring tested code. If we are not familiar, afterwards suppose removing absolved of deployment stress, a “code-and-pray” coding character and everlasting underline development. The best partial of? It’s automatic.
Unit contrast improves code’s orthogonality. Fundamentally, formula is called “orthogonal” when it’s easy to change. Fixing a bug or adding a underline entails zero yet changing a code’s behavior, as explained in The Pragmatic Programmer: From Journeyman to Master6. Unit tests severely urge code’s orthogonality by forcing we to write modular proof units, instead of vast formula chunks.
Unit contrast also provides we with support that is always adult to date and that informs we about a code’s intentions and organic behavior. Even if a routine has a mysterious name — that is bad, yet we won’t get into that here — you’ll now know what it does by reading a test.
Unit contrast has another vital advantage. It army we to indeed use your formula and detect settlement flaws and bad smells. Take functions. What improved approach to make certain that functions are uncoupled from a rest of your formula than by being means to exam them yet any boilerplate code?
Furthermore, unit contrast opens a doorway to test-driven development. While it’s not this article’s topic, we can’t highlight adequate that test-driven growth is a smashing and prolific approach to write code.
What and What Not to Test
Tests contingency conclude a code’s API. This is a one component that will beam us by this journey. An AngularJS concentration is, by definition, stoical of modules. The facile bricks are materialized by opposite concepts associated to a granularity during that we demeanour during them. At a concentration level, these bricks are AngularJS’ modules. At a procedure level, they are directives, controllers, services, filters and factories. Each one of them is means to promulgate with another by a outmost interface.
All of these bricks share a common attribute. They act as black boxes, that means that they have a middle duty and an outdoor interface materialized by inputs and outputs. This is precisely what territory tests are for: to exam bricks’ outdoor interfaces.
Ignoring a internals as most as probable is deliberate good practice. Unit contrast — and contrast in ubiquitous — is a decoction of stimuli and reactions.
Bootstrapping A Test Environment For AngularJS
To set adult a decent contrast sourroundings for your AngularJS application, we will need several npm modules. Let’s take a discerning peek during them.
Karma: The Spectacular Test Runner
Karma112 is an engine that runs tests opposite code. Although it has been created for AngularJS, it’s not privately tied to it and can be used for any JavaScript application. It’s rarely configurable by a JSON record and a use of several plugins.
All of a examples in this essay can be found in a dedicated GitHub project14, along with a following settlement record for Karma.
// Karma configuration
// Generated on Mon Jul 21 2014 11:48:34 GMT+0200 (CEST)
module.exports = function(config)
config.set(
// bottom trail used to solve all patterns (e.g. files, exclude)
basePath: '',
// frameworks to use
frameworks: ['mocha', 'sinon-chai'],
// list of files / patterns to bucket in a browser
files: [
'bower_components/angular/angular.js',
'bower_components/angular-mocks/angular-mocks.js',
'src/*.js',
'test/*.mocha.js'
],
// list of files to exclude
exclude: [],
// preprocess relating files before portion them to a browser
preprocessors:
'src/*.js': ['coverage']
,
coverageReporter:
type: 'text-summary',
dir: 'coverage/'
,
// exam formula contributor to use
reporters: ['progress', 'coverage'],
// web server port
port: 9876,
// capacitate / invalidate colors in a outlay (reporters and logs)
colors: true,
// turn of logging
logLevel: config.LOG_INFO,
// capacitate / invalidate examination record and executing tests on record changes
autoWatch: true,
// start these browsers
browsers: ['PhantomJS'],
// Continuous Integration mode
// if true, Karma captures browsers, runs a tests and exits
singleRun: false
);
;
This record can be automagically generated by typing karma init
in a depot window. The accessible keys are described in Karma’s documentation15.
Notice how sources and exam files are declared. There is also a newcomer: ngMock16 (i.e. angular-mocks.js
). ngMock is an AngularJS procedure that provides several contrast utilities (more on that during a finish of this article).
Mocha
Mocha173 is a contrast horizon for JavaScript. It handles exam suites and exam cases, and it offers good stating features. It uses a declarative syntax to nest expectations into cases and suites. Let’s demeanour during a following instance (shamelessly stolen from Mocha’s home page):
describe('Array', function()
describe('#indexOf()', function()
it('should lapse -1 when a value is not present', function()
assert.equal(-1, [1,2,3].indexOf(5));
assert.equal(-1, [1,2,3].indexOf(0));
);
);
);
You can see that a whole exam is contained in a describe
call. What is engaging about nesting duty calls in this approach is that a tests follow a code’s structure. Here, a Array
apartment is stoical of customarily one subsuite, #indexOf
. Others could be added, of course. This subsuite is stoical of one case, that itself contains dual assertions and expectations. Organizing exam suites into a awake whole is essential. It ensures that exam errors will be reported with suggestive messages, so easing a debugging process.
Chai
We have seen how Mocha provides test-suite and test-case capabilities for JavaScript. Chai184, for a part, offers several ways of checking things in exam cases. These checks are achieved by what are called “assertions” and fundamentally symbol a exam box as unsuccessful or passed. Chai’s support has more19 on a opposite assertions styles.
Sinon
Sinon205 describes itself as “standalone exam spies, stubs and mocks for JavaScript.” Spies, stubs and mocks all answer a same question: How do we good reinstate one thing with another when regulating a test? Suppose we have a duty that takes another one in a parameter and calls it. Sinon provides a intelligent and obvious approach to guard possibly a duty is called and most some-more (with that arguments, how many times, etc.).
Unit Testing At The Application Level
The indicate of a outmost interface of a procedure in an AngularJS concentration is a ability to be injected into another procedure — that it exists and has a current definition.
beforeEach(module('myAwesomeModule'));
This is adequate and will chuck an blunder if myAwesomeModule
is nowhere to be found.
Unit Testing At The Module Level
An AngularJS procedure can announce several forms of objects. Some are services, while others are some-more specialized. We will go over any of them to see how they can be bootstrapped in a tranquil sourroundings and afterwards tested.
Filters, Services and Factories: A Story of Dependency Injection
Filters, services and factories (we will impute to these as services in general) can be compared to immobile objects or singletons in a normal object-oriented framework. They are easy to exam since they need really few things to be ready, and these things are customarily other services.
AngularJS links services to other services or objects regulating a really fluent dependency-injection model, that fundamentally means seeking for something in a method’s arguments.
What is good about AngularJS’ approach of injecting dependencies is that derisive a square of code’s dependencies and injecting things into exam cases are super-easy. In fact, we am not even certain it could be any simpler. Let’s cruise this utterly useful factory:
angular.module('factories', [])
.factory('chimp', ['$log', function($log)
lapse
ook: function()
$log.warn('Ook.');
;
]);
See how $log
is injected, instead of a customary console.warn
? While AngularJS will not imitation $log
statements in Karma’s console, equivocate side effects in territory tests as most as possible. we once reduced by half a generation of an application’s territory tests by derisive a tracking HTTP requests — that were all silently unwell in a internal environment, obviously.
describe('factories', function()
beforeEach(module('factories'));
var chimp;
var $log;
beforeEach(inject(function(_chimp_, _$log_)
chimp = _chimp_;
$log = _$log_;
sinon.stub($log, 'warn', function() );
));
describe('when invoked', function()
beforeEach(function()
chimp.ook();
);
it('should contend Ook', function()
expect($log.warn.callCount).to.equal(1);
expect($log.warn.args[0][0]).to.equal('Ook.');
);
);
);
The settlement for contrast filters, services or other injectables is a same. Controllers can be a bit trickier to test, though, as we will see now.
Controllers
Testing a controller could lead to some confusion. What do we test? Let’s concentration on what a controller is ostensible to do. You should be used to deliberation any tested component as a black box by now. Remember that AngularJS is a model-view-whatever (MVW) framework, that is kind of mocking since one of a few ways to conclude something in an AngularJS concentration is to use a keyword controller
. Still, any kind of decent controller customarily acts as a substitute between a indication and a view, by objects in one approach and callbacks in a other.
The controller customarily configures a perspective regulating some state objects, such as a following (for a suppositious text-editing application):
angular.module('textEditor', [])
.controller('EditionCtrl', ['$scope', function($scope)
$scope.state = toolbarVisible: true, documentSaved: true;
$scope.document = text: 'Some text';
$scope.$watch('document.text', function(value)
$scope.state.documentSaved = false;
, true);
$scope.saveDocument = function()
$scope.sendHTTP($scope.document.text);
$scope.state.documentSaved = true;
;
$scope.sendHTTP = function(content)
// cargo creation, HTTP request, etc.
;
]);
Chances are that a state will be mutated by both a perspective and a controller. The toolbarVisible
charge will be toggled by, say, a symbol and a keyboard shortcut. Unit tests are not ostensible to exam interactions between a perspective and a rest of a universe; that is what end-to-end tests are for.
The documentSaved
value will be mostly rubbed by a controller, though. Let’s exam it.
describe('saving a document', function()
var scope;
var ctrl;
beforeEach(module('textEditor'));
beforeEach(inject(function($rootScope, $controller)
range = $rootScope.$new();
ctrl = $controller('EditionCtrl', $scope: scope);
));
it('should have an initial documentSaved state', function()
expect(scope.state.documentSaved).to.equal(true);
);
describe('documentSaved property', function()
beforeEach(function()
// We don't wish additional HTTP requests to be sent
// and that's not what we're contrast here.
sinon.stub(scope, 'sendHTTP', function() );
// A call to $apply() contingency be performed, differently the
// scope's watchers won't be run through.
scope.$apply(function ()
scope.document.text += ' And some some-more text';
);
);
it('should watch for document.text changes', function()
expect(scope.state.documentSaved).to.equal(false);
);
describe('when job a saveDocument function', function()
beforeEach(function()
scope.saveDocument();
);
it('should be set to loyal again', function()
expect(scope.state.documentSaved).to.equal(true);
);
afterEach(function()
expect(scope.sendHTTP.callCount).to.equal(1);
expect(scope.sendHTTP.args[0][0]).to.equal(scope.document.text);
);
);
);
);
An engaging side outcome of this formula cube is that it not customarily tests changes on a documentSaved
property, yet also checks that a sendHTTP
routine indeed gets called and with a correct arguments (we will see after how to exam HTTP requests). This is because it’s a distant routine published on a controller’s scope. Decoupling and avoiding pseudo-global states (i.e. flitting a calm to a method, instead of vouchsafing it review a calm on a scope) always eases a routine of essay tests.
Directives
A gauge is AngularJS’ approach of training HTML new tricks and of encapsulating a proof behind those tricks. This encapsulation has several hit points with a outward that are tangible in a returned object’s scope
attribute. The categorical disproportion with territory contrast a controller is that directives customarily have an removed scope, yet they both act as a black box and, therefore, will be tested in roughly a same manner. The test’s settlement is a bit different, though.
Let’s suppose a gauge that displays a div
with some fibre inside of it and a symbol subsequent to it. It could be implemented as follows:
angular.module('myDirectives', [])
.directive('superButton', function()
lapse
scope: label: '=', callback: 'onClick',
replace: true,
restrict: 'E',
link: function(scope, element, attrs)
,
template: 'div' +
'divlabel/div' +
'button ng-click="callback()"Click me!/button' +
'/div'
;
);
We wish to exam dual things here. The initial thing to exam is that a tag gets scrupulously upheld to a initial div
’s content, and a second is that something happens when a symbol gets clicked. It’s value observant that a tangible digest of a gauge belongs somewhat some-more to end-to-end and organic testing, yet we wish to embody it as most as probable in a territory tests simply for a consequence of unwell fast. Besides, operative with test-driven growth is easier with territory tests than with higher-level tests, such as functional, formation and end-to-end tests.
describe('directives', function()
beforeEach(module('myDirectives'));
var element;
var outerScope;
var innerScope;
beforeEach(inject(function($rootScope, $compile)
component = angular.element('super-button label="myLabel" on-click="myCallback()"/super-button');
outerScope = $rootScope;
$compile(element)(outerScope);
innerScope = element.isolateScope();
outerScope.$digest();
));
describe('label', function()
beforeEach(function()
outerScope.$apply(function()
outerScope.myLabel = "Hello world.";
);
)
it('should be rendered', function()
expect(element[0].children[0].innerHTML).to.equal('Hello world.');
);
);
describe('click callback', function()
var mySpy;
beforeEach(function()
mySpy = sinon.spy();
outerScope.$apply(function()
outerScope.myCallback = mySpy;
);
);
describe('when a gauge is clicked', function()
beforeEach(function()
var eventuality = document.createEvent("MouseEvent");
event.initMouseEvent("click", true, true);
element[0].children[1].dispatchEvent(event);
);
it('should be called', function()
expect(mySpy.callCount).to.equal(1);
);
);
);
);
This instance has something important. We saw that territory tests make refactoring easy as pie, yet we didn’t see how exactly. Here, we are contrast that when a click happens on a button, a duty upheld as a on-click
charge is called. If we take a closer demeanour during a directive’s code, we will see that this duty gets locally renamed to callback
. It’s published underneath this name on a directive’s removed scope. We could write a following test, then:
describe('click callback', function()
var mySpy;
beforeEach(function()
mySpy = sinon.spy();
innerScope.callback = mySpy;
);
describe('when a gauge is clicked', function()
beforeEach(function()
var eventuality = document.createEvent("MouseEvent");
event.initMouseEvent("click", true, true);
element[0].children[1].dispatchEvent(event);
);
it('should be called', function()
expect(mySpy.callCount).to.equal(1);
);
);
);
And it would work, too. But afterwards we wouldn’t be contrast a outmost aspect of a directive. If we were to forget to supplement a correct pivotal to a directive’s scope
definition, afterwards no exam would stop us. Besides, we indeed don’t caring possibly a gauge renames a callback or calls it by another routine (and if we do, afterwards it will have to be tested elsewhere anyway).
Providers
This is a toughest of a small series. What is a provider exactly? It’s AngularJS’ possess approach of wiring things together before a concentration starts. A provider also has a bureau facet — in fact, we substantially know a $routeProvider
and a small brother, a $route
factory. Let’s write a possess provider and a bureau and afterwards exam them!
angular.module('myProviders', [])
.provider('coffeeMaker', function()
var useFrenchPress = false;
this.useFrenchPress = function(value)
if (value !== undefined)
useFrenchPress = !!value;
lapse useFrenchPress;
;
this.$get = duty ()
lapse
brew: function()
lapse useFrenchPress ? 'Le café.': 'A coffee.';
;
;
);
There’s zero imagination in this super-useful provider, that defines a dwindle and a accessor method. We can see a config partial and a bureau partial (which is returned by a $get
method). we won’t go over a provider’s whole doing and use cases, yet we inspire we to demeanour during AngularJS’ central support about providers21.
To exam this provider, we could exam a config partial on a one palm and a bureau partial on a other. This wouldn’t be deputy of a approach a provider is generally used, though. Let’s cruise about a approach that we use providers. First, we do some configuration; then, we use a provider’s bureau in some other objects or services. We can see in a coffeeMaker
that a duty depends on a useFrenchPress
flag. This is how we will proceed. First, we will set this flag, and afterwards we’ll play with a bureau to see possibly it behaves accordingly.
describe('coffee builder provider', function()
var coffeeProvider = undefined;
beforeEach(function()
// Here we emanate a feign procedure customarily to prevent and store a provider
// when it's injected, i.e. during a config phase.
angular.module('dummyModule', function() )
.config(['coffeeMakerProvider', function(coffeeMakerProvider)
coffeeProvider = coffeeMakerProvider;
]);
module('myProviders', 'dummyModule');
// This indeed triggers a injection into dummyModule
inject(function());
);
describe('with french press', function()
beforeEach(function()
coffeeProvider.useFrenchPress(true);
);
it('should remember a value', function()
expect(coffeeProvider.useFrenchPress()).to.equal(true);
);
it('should make some coffee', inject(function(coffeeMaker)
expect(coffeeMaker.brew()).to.equal('Le café.');
));
);
describe('without french press', function()
beforeEach(function()
coffeeProvider.useFrenchPress(false);
);
it('should remember a value', function()
expect(coffeeProvider.useFrenchPress()).to.equal(false);
);
it('should make some coffee', inject(function(coffeeMaker)
expect(coffeeMaker.brew()).to.equal('A coffee.');
));
);
);
HTTP Requests
HTTP requests are not accurately on a same turn as providers or controllers. They are still an essential partial of territory testing, though. If we do not have a singular HTTP ask in your whole app, afterwards we can skip this section, we propitious fellow.
Roughly, HTTP requests act like inputs and outputs during any of your application’s level. In a RESTfully designed system, GET
requests give information to a app, and PUT
, POST
and DELETE
methods take some. That is what we wish to test, and luckily AngularJS creates that easy.
Let’s take a bureau instance and supplement a POST
ask to it:
angular.module('factories_2', [])
.factory('chimp', ['$http', function($http)
lapse
sendMessage: function()
$http.post('http://chimps.org/messages', message: 'Ook.');
;
]);
We apparently do not wish to exam this on a tangible server, nor do we wish to monkey-patch a XMLHttpRequest constructor. That is where $httpBackend
enters a game.
describe('http', function()
beforeEach(module('factories_2'));
var chimp;
var $httpBackend;
beforeEach(inject(function(_chimp_, _$httpBackend_)
chimp = _chimp_;
$httpBackend = _$httpBackend_;
));
describe('when promulgation a message', function()
beforeEach(function()
$httpBackend.expectPOST('http://chimps.org/messages', message: 'Ook.')
.respond(200, message: 'Ook.', id: 0);
chimp.sendMessage();
$httpBackend.flush();
);
it('should send an HTTP POST request', function()
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
);
);
);
You can see that we’ve tangible that calls should be released to a feign server and how to respond to them before doing anything else. This is useful and enables us to exam a app’s response to opposite requests’ responses (for example, how does a concentration act when a login ask earnings a 404?). This sold instance simulates a customary POST
response.
The dual other lines of a beforeEach
retard are a duty call and a newcomer, $httpBackend.flush()
. The feign server does not immediately answer any request; instead, it lets we check any surrogate state that we competence have configured. It waits for we to categorically tell it to respond to any tentative ask it competence have received.
The exam itself has dual methods calls on a feign server (verifyNoOutstandingExpectation
and verifyNoOutstandingRequest
). AngularJS’ $httpBackend
does not make despotic equivalence between what it expects and what it indeed receives unless you’ve told it to do so. You can courtesy these lines as dual expectations, one of a series of tentative requests and a other of a series of tentative expectations.
ngMock Module
The ngMock module22 contains several utilities to assistance we well-spoken over JavaScript and AngularJS’ specifics.
$timeout, $log and a Others
Using AngularJS’ injectable dependencies is improved than accessing tellurian objects such as console
or window
. Let’s cruise console
calls. They are outputs customarily like HTTP requests and competence indeed matter if we are implementing an API for that some errors contingency be logged. To exam them, we can possibly monkey-patch a tellurian intent — yikes! — or use AngularJS’ good injectable.
The $timeout
dependency also provides a really available flush()
method, customarily like $httpBackend
. If we emanate a bureau that provides a approach to quickly set a dwindle to true
and afterwards revive it to a strange value, afterwards a correct approach to exam it’s to use $timeout
.
angular.module('timeouts', [])
.factory('waiter', ['$timeout', function($timeout)
lapse
brieflySetSomethingToTrue: function(target, property)
var oldValue = target[property];
target[property] = true;
$timeout(function()
target[property] = oldValue;
, 100);
;
]);
And a exam will demeanour like this:
describe('timeouts', function()
beforeEach(module('timeouts'));
var waiter;
var $timeout;
beforeEach(inject(function(_waiter_, _$timeout_)
waiter = _waiter_;
$timeout = _$timeout_;
));
describe('brieflySetSomethingToTrue method', function()
var anyObject;
beforeEach(function()
anyObject = foo: 42;
waiter.brieflySetSomethingToTrue(anyObject, 'foo');
);
it('should quickly set something to true', function()
expect(anyObject.foo).to.equal(true);
$timeout.flush();
expect(anyObject.foo).to.equal(42);
);
);
);
Notice how we’re checking a surrogate state and afterwards flush()
’ing a timeout.
module() and inject()
The module()23 and inject()24 functions assistance to collect modules and dependencies during tests. The former enables we to collect a module, while a latter creates an instance of $injector
, that will solve references.
it('should contend Ook.', inject(function($log)
sinon.stub($log, 'warn', function() );
chimp.ook();
expect($log.warn.callCount).to.equal(1);
expect($log.warn.args[0][0]).to.equal('Ook.');
));
In this exam case, we’re jacket a exam box duty in an inject
call. This call will emanate an $injector
instance and solve any dependencies announced in a exam box function’s arguments.
Dependency Injection Made Easy
One final pretence is to ask for dependencies regulating underscores around a name of what we are seeking for. The indicate of this is to allot a internal non-static that has a same name as a dependencies. Indeed, a $injector
used in a tests will mislay surrounding underscores if any are found. StackOverflow has a comment25 on this.
Conclusion
Unit contrast in AngularJS applications follows a fractal design. It tests units of code. It freezes a unit’s duty by providing a approach to automatically check a response to a given input. Note that territory tests do not reinstate good coding. AngularJS’ support is flattering transparent on this point: “Angular is created with testability in mind, yet it still requires that we do a right thing.”
Getting started with essay territory tests — and coding in test-driven growth — is hard. However, a advantages will shortly uncover adult if you’re peaceful to entirely exam your application, generally during refactoring operations.
Tests also work good with flexible methods. User stories are roughly tests; they’re customarily not tangible formula (although some approaches, such as “design by contract26,” minimize this difference).
Further Resources
- “The Pragmatic Programmer: From Journeyman to Master27,” Andrew Hunt and David Thomas
- AngularJS’ support on territory testing28
- All examples can be found in a GitHub repository29
(al, ml)
Footnotes
- 1 https://angularjs.org
- 2 http://karma-runner.github.io
- 3 http://visionmedia.github.io/mocha/
- 4 http://chaijs.com
- 5 http://sinonjs.org
- 6 https://pragprog.com/the-pragmatic-programmer
- 7 http://www.smashingmagazine.com/wp-content/uploads/2014/09/01-bricks-opt.png
- 8 http://www.smashingmagazine.com/wp-content/uploads/2014/09/01-bricks-opt.png
- 9 http://www.smashingmagazine.com/wp-content/uploads/2014/09/02-blackbox-opt.png
- 10 http://www.smashingmagazine.com/wp-content/uploads/2014/09/02-blackbox-opt.png
- 11 http://karma-runner.github.io
- 12 http://www.smashingmagazine.com/wp-content/uploads/2014/09/03-karma-success-opt.png
- 13 http://www.smashingmagazine.com/wp-content/uploads/2014/09/03-karma-success-opt.png
- 14 https://github.com/lorem–ipsum/smashing-article
- 15 http://karma-runner.github.io/0.8/config/configuration-file.html
- 16 https://docs.angularjs.org/api/ngMock
- 17 http://visionmedia.github.io/mocha/
- 18 http://chaijs.com
- 19 http://chaijs.com/guide/styles/
- 20 http://sinonjs.org
- 21 https://docs.angularjs.org/guide/providers
- 22 https://docs.angularjs.org/api/ngMock
- 23 https://docs.angularjs.org/api/ngMock/function/angular.mock.module
- 24 https://docs.angularjs.org/api/ngMock/function/angular.mock.inject
- 25 http://stackoverflow.com/a/15318137/863119
- 26 http://en.wikipedia.org/wiki/Design_by_contract
- 27 https://pragprog.com/the-pragmatic-programmer
- 28 https://docs.angularjs.org/guide/unit-testing
- 29 https://github.com/lorem–ipsum/smashing-article
↑ Back to topShare on Twitter
An Introduction To Unit Testing In AngularJS Applications
Nenhum comentário:
Postar um comentário