mirror of
https://github.com/zhigang1992/angular.js.git
synced 2026-04-23 19:40:56 +08:00
docs - stripping extra new lines
This commit is contained in:
@@ -3,7 +3,6 @@
|
||||
@name Developer Guide: Unit Testing
|
||||
@description
|
||||
|
||||
|
||||
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
|
||||
written in JavaScript needs to come with a strong set of tests. We have built many features into
|
||||
@@ -27,7 +26,6 @@ DOM in the right way. Angular is written with testability in mind, but it still
|
||||
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, you may very well end up with an untestable application.
|
||||
|
||||
|
||||
## Dependency Inject
|
||||
There are several ways in which you can get a hold of a dependency:
|
||||
1. You could create it using the `new` operator.
|
||||
@@ -36,18 +34,14 @@ There are several ways in which you can get a hold of a dependency:
|
||||
the registry? Must likely by looking it up in a well know place. See #2)
|
||||
4. You could expect that the it be handed to you.
|
||||
|
||||
|
||||
Out of the list above only the last of is testable. Lets look at why:
|
||||
|
||||
|
||||
### Using the `new` operator
|
||||
|
||||
|
||||
While there is nothing wrong with the `new` 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 `XHR` so that we can get some data from the server.
|
||||
|
||||
|
||||
<pre>
|
||||
function MyClass(){
|
||||
this.doWork = function(){
|
||||
@@ -59,13 +53,11 @@ function MyClass(){
|
||||
}
|
||||
</pre>
|
||||
|
||||
|
||||
The issue becomes, that in tests, we would very much like to instantiate a `MockXHR` which would
|
||||
allow us to return fake data and simulate network failures. By calling `new XHR()` we are
|
||||
permanently bound to the actual one, and there is no good way to replace it. Yes there is monkey
|
||||
patching, that is a bad idea for many reasons, which is outside the scope of this document.
|
||||
|
||||
|
||||
The class above is hard to test since we have to resort to monkey patching:
|
||||
<pre>
|
||||
var oldXHR = XHR;
|
||||
@@ -77,12 +69,9 @@ XHR = oldXHR; // if you forget this bad things will happen
|
||||
</pre>
|
||||
|
||||
|
||||
|
||||
|
||||
### Global look-up:
|
||||
Another way to approach the problem is look for the service in a well known location.
|
||||
|
||||
|
||||
<pre>
|
||||
function MyClass(){
|
||||
this.doWork = function(){
|
||||
@@ -95,7 +84,6 @@ function MyClass(){
|
||||
}
|
||||
</pre>
|
||||
|
||||
|
||||
While no new instance of dependency is being created, it is fundamentally the same as `new`, in
|
||||
that there is no good way to intercept the call to `global.xhr` for testing purposes, other then
|
||||
through monkey patching. The basic issue for testing is that global variable needs to be mutated in
|
||||
@@ -103,7 +91,6 @@ order to replace it with call to a mock method. For further explanation why this
|
||||
http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/ Brittle Global
|
||||
State & Singletons}
|
||||
|
||||
|
||||
The class above is hard to test since we have to change global state:
|
||||
<pre>
|
||||
var oldXHR = glabal.xhr;
|
||||
@@ -115,15 +102,11 @@ global.xhr = oldXHR; // if you forget this bad things will happen
|
||||
</pre>
|
||||
|
||||
|
||||
|
||||
|
||||
### Service Registry:
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
<pre>
|
||||
function MyClass() {
|
||||
var serviceRegistry = ????;
|
||||
@@ -137,13 +120,11 @@ function MyClass() {
|
||||
}
|
||||
</pre>
|
||||
|
||||
|
||||
However, where dose the serviceRegistry come from? if it is:
|
||||
* `new`-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
|
||||
there is only one global variable to be reset).
|
||||
|
||||
|
||||
The class above is hard to test since we have to change global state:
|
||||
<pre>
|
||||
var oldServiceLocator = glabal.serviceLocator;
|
||||
@@ -155,12 +136,9 @@ glabal.serviceLocator = oldServiceLocator; // if you forget this bad things will
|
||||
</pre>
|
||||
|
||||
|
||||
|
||||
|
||||
### Passing in Dependencies:
|
||||
Lastly the dependency can be passed in.
|
||||
|
||||
|
||||
<pre>
|
||||
function MyClass(xhr) {
|
||||
this.doWork = function(){
|
||||
@@ -227,11 +205,9 @@ function PasswordController(){
|
||||
}
|
||||
</pre>
|
||||
|
||||
|
||||
The code above is problematic from testability, since it requires your test to have the right kind
|
||||
of DOM present when the code executes. The test would look like this:
|
||||
|
||||
|
||||
<pre>
|
||||
var input = $('<input type="text"/>');
|
||||
var span = $('<span>');
|
||||
@@ -246,11 +222,9 @@ expect(span.text()).toEqual('weak');
|
||||
$('body').html('');
|
||||
</pre>
|
||||
|
||||
|
||||
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:
|
||||
|
||||
|
||||
<pre>
|
||||
function PasswordCntrl(){
|
||||
this.password = '';
|
||||
@@ -267,10 +241,8 @@ function PasswordCntrl(){
|
||||
}
|
||||
</pre>
|
||||
|
||||
|
||||
and the tests is straight forward
|
||||
|
||||
|
||||
<pre>
|
||||
var pc = new PasswordController();
|
||||
pc.password('abc');
|
||||
@@ -278,37 +250,29 @@ pc.grade();
|
||||
expect(span.strength).toEqual('weak');
|
||||
</pre>
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
## Filters
|
||||
{@link api/angular.filter Filters} 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.
|
||||
|
||||
|
||||
<pre>
|
||||
angular.filter('length', function(text){
|
||||
return (''+(text||'')).length;
|
||||
});
|
||||
|
||||
|
||||
var length = angular.filter('length');
|
||||
expect(length(null)).toEqual(0);
|
||||
expect(length('abc')).toEqual(3);
|
||||
</pre>
|
||||
|
||||
|
||||
## Directives
|
||||
Directives in angular are responsible for updating the DOM when the state of the model changes.
|
||||
|
||||
|
||||
|
||||
|
||||
## Mocks
|
||||
oue
|
||||
## Global State Isolation
|
||||
|
||||
Reference in New Issue
Block a user