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(https://www.jb51.net/article/91775.htm),通过这种方式编写的代码,更加容易编写测试代码,如果我们想编写可测试性强的代码,我们最好使用它。
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>').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($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