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
相关文章
相关标签/搜索