Upgrade to angularjs 1.2.0 rc1
This commit is contained in:
parent
d223dfd662
commit
d6b021bfaf
674 changed files with 79667 additions and 62269 deletions
175
lib/angular/docs/partials/guide/dev_guide.unit-testing.html
Normal file → Executable file
175
lib/angular/docs/partials/guide/dev_guide.unit-testing.html
Normal file → Executable file
|
@ -1,27 +1,23 @@
|
|||
<h1><code ng:non-bindable=""></code>
|
||||
<span class="hint"></span>
|
||||
<a href="http://github.com/angular/angular.js/edit/master/docs/content/guide/dev_guide.unit-testing.ngdoc" class="improve-docs btn btn-primary"><i class="icon-edit"> </i> Improve this doc</a><h1><code ng:non-bindable=""></code>
|
||||
<div><span class="hint"></span>
|
||||
</div>
|
||||
</h1>
|
||||
<div><a href="http://github.com/angular/angular.js/edit/master/docs/content/guide/dev_guide.unit-testing.ngdoc" class="improve-docs btn btn-primary">Improve this doc</a><p>JavaScript is a dynamically typed language which comes with great power of expression, but it also
|
||||
come with almost no-help from the compiler. For this reason we feel very strongly that any code
|
||||
<div><div class="developer-guide-page developer-guide-unit-testing-page"><p>JavaScript is a dynamically typed language which comes with great power of expression, but it also
|
||||
comes with almost no help from the compiler. For this reason we feel very strongly that any code
|
||||
written in JavaScript needs to come with a strong set of tests. We have built many features into
|
||||
Angular which makes testing your Angular applications easy. So there is no excuse for not testing.</p>
|
||||
|
||||
<h2>It is all about NOT mixing concerns</h2>
|
||||
|
||||
<p>Unit testing as the name implies is about testing individual units of code. Unit tests try to
|
||||
answer questions such as "Did I think about the logic correctly?" or "Does the sort function order the list
|
||||
in the right order?"</p>
|
||||
|
||||
answer questions such as "Did I think about the logic correctly?" or "Does the sort function order the list
|
||||
in the right order?"</p>
|
||||
<p>In order to answer such question it is very important that we can isolate the unit of code under test.
|
||||
That is because when we are testing the sort function we don't want to be forced into creating
|
||||
That is because when we are testing the sort function we don't want to be forced into creating
|
||||
related pieces such as the DOM elements, or making any XHR calls in getting the data to sort.</p>
|
||||
|
||||
<p>While
|
||||
this may seem obvious it usually is very difficult to be able to call an individual function on a
|
||||
typical project. The reason is that the developers often mix concerns, and they end up with a
|
||||
piece of code which does everything. It reads the data from XHR, it sorts it and then it
|
||||
manipulates the DOM.</p>
|
||||
|
||||
<p>With Angular we try to make it easy for you to do the right thing, and so we
|
||||
provide dependency injection for your XHR (which you can mock out) and we created abstraction which
|
||||
allow you to sort your model without having to resort to manipulating the DOM. So that in the end,
|
||||
|
@ -29,30 +25,22 @@ it is easy to write a sort function which sorts some data, so that your test can
|
|||
apply the function, and assert that the resulting model is in the correct order. The test does not
|
||||
have to wait for XHR, or create the right kind of DOM, or assert that your function has mutated the
|
||||
DOM in the right way.</p>
|
||||
|
||||
<h3>With great power comes great responsibility</h3>
|
||||
|
||||
<h3>With great power comes great responsibility</h2>
|
||||
<p>Angular is written with testability in mind, but it still requires that you
|
||||
do the right thing. We tried to make the right thing easy, but Angular is not magic, which means if
|
||||
you don't follow these guidelines you may very well end up with an untestable application.</p>
|
||||
|
||||
<h3>Dependency Injection</h3>
|
||||
|
||||
you don't follow these guidelines you may very well end up with an untestable application.</p>
|
||||
<h2>Dependency Injection</h2>
|
||||
<p>There are several ways in which you can get a hold of a dependency:
|
||||
1. You could create it using the <code>new</code> operator.
|
||||
2. You could look for it in a well known place, also known as global singleton.
|
||||
3. You could ask a registry (also known as service registry) for it. (But how do you get a hold of
|
||||
the registry? Most likely by looking it up in a well known place. See #2)
|
||||
4. You could expect that it be handed to you.</p>
|
||||
|
||||
<p>Out of the four options in the list above, only the last one is testable. Let's look at why:</p>
|
||||
|
||||
<h4>Using the <code>new</code> operator</h4>
|
||||
|
||||
<p>Out of the four options in the list above, only the last one is testable. Let's look at why:</p>
|
||||
<h4>Using the <code>new</code> operator</h3>
|
||||
<p>While there is nothing wrong with the <code>new</code> operator fundamentally the issue is that calling a new
|
||||
on a constructor permanently binds the call site to the type. For example lets say that we are
|
||||
trying to instantiate an <code>XHR</code> so that we can get some data from the server.</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
function MyClass() {
|
||||
this.doWork = function() {
|
||||
|
@ -63,12 +51,10 @@ function MyClass() {
|
|||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>The issue becomes that in tests, we would very much like to instantiate a <code>MockXHR</code> which would
|
||||
allow us to return fake data and simulate network failures. By calling <code>new XHR()</code> we are
|
||||
permanently bound to the actual XHR, and there is no good way to replace it. Yes there is monkey
|
||||
patching. That is a bad idea for many reasons which are outside the scope of this document.</p>
|
||||
|
||||
<p>The class above is hard to test since we have to resort to monkey patching:
|
||||
<pre class="prettyprint linenums">
|
||||
var oldXHR = XHR;
|
||||
|
@ -78,11 +64,8 @@ myClass.doWork();
|
|||
// assert that MockXHR got called with the right arguments
|
||||
XHR = oldXHR; // if you forget this bad things will happen
|
||||
</pre>
|
||||
|
||||
<h4>Global look-up:</h4>
|
||||
|
||||
<h3>Global look-up:</h3>
|
||||
<p>Another way to approach the problem is to look for the service in a well known location.</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
function MyClass() {
|
||||
this.doWork = function() {
|
||||
|
@ -94,12 +77,10 @@ function MyClass() {
|
|||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>While no new instance of the dependency is being created, it is fundamentally the same as <code>new</code>, in
|
||||
that there is no good way to intercept the call to <code>global.xhr</code> for testing purposes, other then
|
||||
through monkey patching. The basic issue for testing is that global variable needs to be mutated in
|
||||
through monkey patching. The basic issue for testing is that a global variable needs to be mutated in
|
||||
order to replace it with call to a mock method. For further explanation why this is bad see: <a href="http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/">Brittle Global State & Singletons</a></p>
|
||||
|
||||
<p>The class above is hard to test since we have to change global state:
|
||||
<pre class="prettyprint linenums">
|
||||
var oldXHR = global.xhr;
|
||||
|
@ -109,12 +90,9 @@ myClass.doWork();
|
|||
// assert that mockXHR got called with the right arguments
|
||||
global.xhr = oldXHR; // if you forget this bad things will happen
|
||||
</pre>
|
||||
|
||||
<h4>Service Registry:</h4>
|
||||
|
||||
<h3>Service Registry:</h3>
|
||||
<p>It may seem as that this can be solved by having a registry for all of the services, and then
|
||||
having the tests replace the services as needed.</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
function MyClass() {
|
||||
var serviceRegistry = ????;
|
||||
|
@ -127,12 +105,10 @@ function MyClass() {
|
|||
})
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>However, where does the serviceRegistry come from? if it is:
|
||||
* <code>new</code>-ed up, the the test has no chance to reset the services for testing
|
||||
* global look-up, then the service returned is global as well (but resetting is easier, since
|
||||
<em> <code>new</code>-ed up, the the test has no chance to reset the services for testing
|
||||
</em> global look-up, then the service returned is global as well (but resetting is easier, since
|
||||
there is only one global variable to be reset).</p>
|
||||
|
||||
<p>The class above is hard to test since we have to change global state:
|
||||
<pre class="prettyprint linenums">
|
||||
var oldServiceLocator = global.serviceLocator;
|
||||
|
@ -142,11 +118,8 @@ myClass.doWork();
|
|||
// assert that mockXHR got called with the right arguments
|
||||
global.serviceLocator = oldServiceLocator; // if you forget this bad things will happen
|
||||
</pre>
|
||||
|
||||
<h4>Passing in Dependencies:</h4>
|
||||
|
||||
<h3>Passing in Dependencies:</h3>
|
||||
<p>Lastly the dependency can be passed in.</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
function MyClass(xhr) {
|
||||
this.doWork = function() {
|
||||
|
@ -157,12 +130,10 @@ function MyClass(xhr) {
|
|||
})
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>This is the preferred way since the code makes no assumptions as to where the <code>xhr</code> comes from,
|
||||
rather that whoever created the class was responsible for passing it in. Since the creator of the
|
||||
class should be different code than the user of the class, it separates the responsibility of
|
||||
creation from the logic, and that is what dependency-injection is in a nutshell.</p>
|
||||
|
||||
<p>The class above is very testable, since in the test we can write:
|
||||
<pre class="prettyprint linenums">
|
||||
function xhrMock(args) {...}
|
||||
|
@ -170,18 +141,13 @@ var myClass = new MyClass(xhrMock);
|
|||
myClass.doWork();
|
||||
// assert that xhrMock got called with the right arguments
|
||||
</pre>
|
||||
|
||||
<p>Notice that no global variables were harmed in the writing of this test.</p>
|
||||
|
||||
<p>Angular comes with <a href="guide/di">dependency injection</a> built in which makes the right thing
|
||||
easy to do, but you still need to do it if you wish to take advantage of the testability story.</p>
|
||||
|
||||
<h3>Controllers</h3>
|
||||
|
||||
<h2>Controllers</h2>
|
||||
<p>What makes each application unique is its logic, which is what we would like to test. If the logic
|
||||
for your application is mixed in with DOM manipulation, it will be hard to test as in the example
|
||||
below:</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
function PasswordCtrl() {
|
||||
// get references to DOM elements
|
||||
|
@ -206,10 +172,8 @@ function PasswordCtrl() {
|
|||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>The code above is problematic from a testability point of view, since it requires your test to have the right kind
|
||||
of DOM present when the code executes. The test would look like this:</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
var input = $('<input type="text"/>');
|
||||
var span = $('<span>');
|
||||
|
@ -223,10 +187,8 @@ pc.grade();
|
|||
expect(span.text()).toEqual('weak');
|
||||
$('body').html('');
|
||||
</pre>
|
||||
|
||||
<p>In angular the controllers are strictly separated from the DOM manipulation logic which results in
|
||||
a much easier testability story as can be seen in this example:</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
function PasswordCtrl($scope) {
|
||||
$scope.password = '';
|
||||
|
@ -242,25 +204,20 @@ function PasswordCtrl($scope) {
|
|||
};
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>and the test is straight forward</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
var pc = new PasswordCtrl();
|
||||
pc.password('abc');
|
||||
pc.grade();
|
||||
expect(pc.strength).toEqual('weak');
|
||||
var $scope = {};
|
||||
var pc = $controller('PasswordCtrl', { $scope: $scope });
|
||||
$scope.password = 'abc';
|
||||
$scope.grade();
|
||||
expect($scope.strength).toEqual('weak');
|
||||
</pre>
|
||||
|
||||
<p>Notice that the test is not only much shorter but it is easier to follow what is going on. We say
|
||||
that such a test tells a story, rather then asserting random bits which don't seem to be related.</p>
|
||||
|
||||
<h3>Filters</h3>
|
||||
|
||||
that such a test tells a story, rather then asserting random bits which don't seem to be related.</p>
|
||||
<h2>Filters</h2>
|
||||
<p><a href="api/ng.$filter"><code>Filters</code></a> are functions which transform the data into user readable
|
||||
format. They are important because they remove the formatting responsibility from the application
|
||||
logic, further simplifying the application logic.</p>
|
||||
|
||||
<pre class="prettyprint linenums">
|
||||
myModule.filter('length', function() {
|
||||
return function(text){
|
||||
|
@ -272,31 +229,57 @@ var length = $filter('length');
|
|||
expect(length(null)).toEqual(0);
|
||||
expect(length('abc')).toEqual(3);
|
||||
</pre>
|
||||
<h2>Directives</h2>
|
||||
<p>Directives in angular are responsible for encapsulating complex functionality within custom HTML tags,
|
||||
attributes, classes or comments. Unit tests are very important for directives because the components
|
||||
you create with directives may be used throughout your application and in many different contexts.</p>
|
||||
<h3>Simple HTML Element Directive</h4>
|
||||
<p>Lets start with an angular app with no dependencies.</p>
|
||||
<pre class="prettyprint linenums">
|
||||
var app = angular.module('myApp', []);
|
||||
</pre>
|
||||
<p>Now we can add a directive to our app.</p>
|
||||
<pre class="prettyprint linenums">
|
||||
app.directive('aGreatEye', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
template: '<h1>lidless, wreathed in flame, {{1 + 1}} times</h1>'
|
||||
};
|
||||
});
|
||||
</pre>
|
||||
<p>This directive is used as a tag <code><a-great-eye></a-great-eye></code>. It replaces the entire tag with the
|
||||
template <code><h1>lidless, wreathed in flame, {{1 + 1}} times</h1></code>. Now we are going to write a jasmine unit test to
|
||||
verify this functionality. Note that the expression <code>{{1 + 1}}</code> times will also be evaluated in the rendered content.</p>
|
||||
<pre class="prettyprint linenums">
|
||||
describe('Unit testing great quotes', function() {
|
||||
var $compile;
|
||||
var $rootScope;
|
||||
|
||||
<h3>Directives</h3>
|
||||
// Load the myApp module, which contains the directive
|
||||
beforeEach(module('myApp'));
|
||||
|
||||
<p>Directives in angular are responsible for updating the DOM when the state of the model changes.</p>
|
||||
|
||||
<h3>Mocks</h3>
|
||||
|
||||
<p>oue</p>
|
||||
|
||||
<h3>Global State Isolation</h3>
|
||||
|
||||
<p>oue</p>
|
||||
|
||||
<h2>Preferred way of Testing</h2>
|
||||
|
||||
<p>uo</p>
|
||||
|
||||
<h3>JavaScriptTestDriver</h3>
|
||||
|
||||
<p>ou</p>
|
||||
|
||||
<h3>Jasmine</h3>
|
||||
|
||||
<p>ou</p>
|
||||
|
||||
<h3>Sample project</h3>
|
||||
|
||||
<p>uoe</p></div>
|
||||
// Store references to $rootScope and $compile
|
||||
// so they are available to all tests in this describe block
|
||||
beforeEach(inject(function(_$compile_, _$rootScope_){
|
||||
// The injector unwraps the underscores (_) from around the parameter names when matching
|
||||
$compile = _$compile_;
|
||||
$rootScope = _$rootScope_;
|
||||
}));
|
||||
|
||||
it('Replaces the element with the appropriate content', function() {
|
||||
// Compile a piece of HTML containing the directive
|
||||
var element = $compile("<a-great-eye></a-great-eye>")($rootScope);
|
||||
// fire all the watches, so the scope expression {{1 + 1}} will be evaluated
|
||||
$rootScope.$digest();
|
||||
// Check that the compiled element contains the templated content
|
||||
expect(element.html()).toContain("lidless, wreathed in flame, 2 times");
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
<p>We inject the $compile service and $rootScope before each jasmine test. The $compile service is used
|
||||
to render the aGreatEye directive. After rendering the directive we ensure that the directive has
|
||||
replaced the content and "lidless, wreathed in flame, 2 times" is present.</p>
|
||||
<h2>Sample project</h3>
|
||||
<p>See the <a href="https://github.com/angular/angular-seed">angular-seed</a> project for an example.</p>
|
||||
</div></div>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue