AngularJs學習筆記--unit-testing

原版地址:http://docs.angularjs.org/guide/dev_guide.unit-testingjavascript

 

  javascript是一門動態類型語言,這給她帶來了很強的表現能力,但同時也使編譯器幾乎不能給開發者提供任何幫助。由於這個緣由,咱們感覺到編寫任何javascript代碼都必須有一套強大完整的測試。angular擁有許多功能,讓咱們更加容易地測試咱們的應用。咱們應該沒有藉口不去寫測試(這個嘛……)。html

1、 It is all about NOT mixing concerns(所有都關於避免代碼關係變得複雜……)java

  單元測試,正如名稱那樣,是關於測試單個「單元」的代碼。單元測試努力解答這些問題:我對邏輯的考慮是否已經正確?排序方法得出的結果是否正確?爲了解答這些問題,將這些問題獨立出來顯得尤爲重要。這是由於當咱們在測試排序方法的時候,咱們不想關心其餘相關的片斷,例如DOM元素或者發起XHR請求獲取數據等。明顯地,一般比較難作到在典型的項目中單獨調用一個函數。致使這個問題的緣由是,開發者一般把關係弄得很複雜,最終讓一個代碼片斷看起來能夠作全部事情。它經過XHR獲取數據,對數據進行排序,而後操縱DOM。與angular一塊兒,咱們能夠更加容易地寫出較好的代碼,因此angular爲咱們提供XHR(咱們能夠模擬它)的依賴注入,angular還建立容許咱們對model進行排序而不須要操做DOM的抽象。因此,到最後,咱們能夠簡單地寫一個排序方法,而後經過測試用例建立數據集合,供排序方法測試時使用,而後判斷結果model是否符合預期。測試無須等待XHR、者建立對應的DOM和判斷函數是否正確操做DOM。angular的核心思想包含代碼的可測試性,但同時也要求咱們去作正確的事情。angular致力於簡化作正確事情的方法,但angular不是魔法,這意味着咱們若是不遵循如下的幾點,咱們最終可能會得出一個不可測試的應用。angularjs

1. Dependency Injectexpress

  有許多辦法能夠得到依賴的資源:1)咱們可使用new操做符;2)咱們使用一個衆所周知的方式,被稱爲」 全局單例」;3)咱們能夠向registry service請求(但咱們如何取得一個registry?能夠查看後面的章節);4)咱們能夠期待它會被傳遞過來。bootstrap

  上面列出的方法中,只有最後一個是可測試的,讓咱們看看爲何:api

1) Using the new operator服務器

  使用new操做符時基本上沒有錯誤,但問題是經過new調用構造函數將會永久地將調用方與type綁定起來。舉個例子,咱們嘗試實例化一個XHR對象,以讓咱們能夠從服務器得到一些數據。網絡

 
function MyClass() {
     this.doWork = function() {
         var xhr = new XRH();
         xhr.open(method,url,true);
         xhr.onreadystatechange = function() {…};
         xhr.send();
  }
}
 

  問題來了,在測試時,咱們一般須要實例化一個能夠返回測試數據或者網絡錯誤的虛擬的XHR。經過調用new XHR(),咱們永久地綁定了真實的XHR,而且沒有一個很好的方法去替代它。固然,有一個糟糕的補救辦法,有不少理由能夠證實那是一個糟糕的想法:app

var oldXHR = XHR;
XHR = new MockXHR() {};
myClass.doWork();
//判斷MockXHR是否經過正常的參數進行調用
XHR = oldXHR;//若是忘了這一步,很容易會發生悲催的事情。

2) Global look-up

  解決問題的另一個方法是在一個衆所周知的地方獲取依賴的資源。

function MyClass() {
     this.doWork = function() {
           global.xhr({…});
    };
}

  沒有建立新依賴對象的實例的狀況下,問題基本上與new一致,除了那個悲催的補丁之外,沒有一個很好的方法能夠再測試時攔截global.xhr的調用。測試的最基本的問題是global變量須要改成調用虛擬的方法而被修改。想進一步瞭解它的壞處,能夠參觀這裏:http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/

  上面的代碼比較難去測試,因此咱們必須修改global state:

var oldXHR = global.xhr;
global.xhr = function mockXHR(){…};
var myClass = new MyClass();
//判斷MockXHR是否經過正常的參數進行調用
global.xhr = oldXHR;//若是忘了這一步,很容易會發生悲催的事情。

3) Service Registry

  擁有一個包含全部service的registry的話,彷佛能夠解決問題,而後,在測試代碼中替換所須要的service。

 
function MyClass() {
     var serviceRegistry = ???;
     this.doWork = function() {
         var xhr = serviceRegistry.get(「xhr」);
     …
    };
}
 

  可是,serviceRegistry來自哪裏?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)(這裏後面的文字跟亂碼同樣……沒看懂)

  根據這個方法,將上面的Class修改成以下的方式:

 
var oldServiceLocator = global.serviceLocator;
global.serviceLocator.set('xhr', function mockXHR() {});
var myClass = new MyClass();
myClass.doWork();
//判斷MockXHR是否經過正常的參數進行調用
global.serviceLocator = oldServiceLocator; //若是忘了這一步,很容易會發生悲催的事情。
 

4) Passing in Dependencies

  最後,依賴資源能夠被傳入。

function MyClass(xhr) {
     this.doWork = function() {
         xhr({…});
    };
}

  這個是首選的方式,由於代碼無須理會xhr是從哪來的,也不關心誰建立了傳進來的xhr。所以,類的建立者與類的使用者能夠分開編碼,這將建立的責任從邏輯中分離出來,這就是依賴注入的概述。

  這個class很容易測試,在測試中咱們能夠這樣寫:

function xhrMock(args) {…}
var myClass = new MyClass(xhrMock);
myClass.doWrok();
//作一些判斷……
經過這個測試代碼,咱們能夠意識到沒有任何全局變量被破壞。

  angular附帶的dependency-injection(http://www.cnblogs.com/lcllao/archive/2012/09/23/2699401.html),經過這種方式編寫的代碼,更加容易編寫測試代碼,若是咱們想編寫可測試性強的代碼,咱們最好使用它。

2. Controllers

  邏輯使每個應用都是惟一的,這就是咱們想去測試的。若是咱們的邏輯裏面混雜着DOM的操做,這將會跟下面的例子同樣難測試:

 
function PasswordController() {
    // 獲取DOM對象的引用
    var msg = $('.ex1 span');
    var input = $('.ex1 input');
    var strength;

    this.grade = function() {
         msg.removeClass(strength);
         var pwd = input.val();
         password.text(pwd);
         if (pwd.length > 8) {
               strength = 'strong';
         } else if (pwd.length > 3) {
               strength = 'medium';
         } else {
               strength = 'weak';
         }
        msg.addClass(strength).text(strength);
    }
}
 

  上面的代碼在測試時會遇到問題,由於它須要咱們的執行測試時候,須要有正確的DOM。測試代碼會以下:

 
var input = $('<input type="text"/>');
var span = $('<span>');
$('body').html('<div class="ex1">').find('div').append(input).append(span);
var pc = new PasswordController();
input.val('abc');
pc.grade();
expect(span.text()).toEqual('weak');
$('body').html('');
 

  在angular中,controller嚴格地將DOM操做邏輯分離出來,將大大下降編寫測試用例的難度,看看下面的例子:

 
function PasswordCntrl($scope) {
    $scope.password = '';
    $scope.grade = function() {
         var size = $scope.password.length;
         if (size > 8) {
                   $scope.strength = 'strong';
         } else if (size > 3) {
                   $scope.strength = 'medium';
         } else {
                   $scope.strength = 'weak';
         }
    };
}
 

  測試代碼直截了當:

var pc = new PasswordController($scope);
pc.password('abc');
pc.grade();
expect($scope.strength).toEqual('weak');

  值得注意的是,測試代碼不只僅更加間斷,並且更加容易追蹤。咱們一直說測試用例是在講故事,而不是判斷其餘不相關的東西。

3. Filters

  filter(http://docs.angularjs.org/api/ng.$filter)是用於將數據轉換爲對用戶友好的格式。它們很重要,由於它們將轉換格式的責任從應用邏輯中分離出來,進一步簡化了應用邏輯。

 
myModule.filter('length', function() {
    return function(text){
         return (''+(text||'')).length;
    }
});

var length = $filter('length');
expect(length(null)).toEqual(0);
expect(length('abc')).toEqual(3);
 

4. Directives

5. Mocks

6. Global State Isolation

7. Preferred way of Testing

8. JavascriptTestDriver

9. Jasmine

10.   Sample project

 

  沒錯,你沒有眼花……官網文檔竟然太監了!……期待更新……順帶說一下,Angular學習筆記—Guide系列暫告一段落!

 

 

 

 

 

 

 

 

 

 

 

  1. AngularJs學習筆記--bootstrap
  2. AngularJs學習筆記--html compiler
  3. AngularJs學習筆記--concepts
  4. AngularJs學習筆記--directive
  5. AngularJs學習筆記--expression
  6. AngularJs學習筆記--Forms
  7. AngularJs學習筆記--I18n/L10n
  8. AngularJs學習筆記--IE Compatibility
  9. AngularJs學習筆記--Modules
  10. AngularJs學習筆記--Scope
  11. AngularJs學習筆記--Dependency Injection
  12. AngularJs學習筆記--Understanding the Model Component
  13. AngularJs學習筆記--Understanding the Controller Component
  14. AngularJs學習筆記--E2E Testing
  15. AngularJs學習筆記--Understanding Angular Templates
  16. AngularJs學習筆記--Using $location
  17. AngularJs學習筆記--Creating Services
  18. AngularJs學習筆記--Injecting Services Into Controllers
  19. AngularJs學習筆記--Managing Service Dependencies
  20. AngularJs學習筆記--unit-testing
相關文章
相關標籤/搜索