SCRUM 敏捷开发方法支持
Problem Domain
Technical Stack
https://github.com/angular-app/angular-app
Regarding files organization strategy,a build system,and basic workflows employed.
关于编译系统:
AngularJS需要处理目录层级和模块层级关系。
允许多个AngularJS 模块放到一个文件中
AngularJS模块也可以分布在多个文件中。
最好每个js文件只定义一个模块。
当我们声明好一个module时,我们就会拿它来注册对象创建配方,这些配方被称为providers。
每个provider在执行时都回产生一个运行时service实例。
这种Provider可能有多种特例比如factory,service,varaible,constant等)
var adminProjects = angular.module('admin-projects', []);
adminProjects.controller('ProjectsListCtrl', function($scope) {
//controller's code go here
});
adminProjects.controller('ProjectsEditCtrl', function($scope) {
//controller's code go here
});
每个module都可以有多个配置和run块。
AngularJS支持两种配置阶段的注册函数,我们可以为angular.module() 配置第三个参数
angular.module('admin-projects',[],function(){
//configuration logic goes here
});
该形式的替代方法是config(),可以调用多次。
angular.module('admin-projects', [])
.config(function() {
//configuration block 1
})
.config(function() {
//configuration block 2
});
如此我们可以把较大的配置块,分拆成多个小块,突出功能主题。
Jasmine test
behavior-driven development framework for testing JavaScript code.
describe('hello world test',function(){
var greeter;
beforeEach(function(){
greeter = new Greeter();
});
it('Should say hello to the world',function(){
expect(greeter.say('world')).toEqual('Hello, World!');
});
});
真正的测试用例隐藏在it体内,而beforeEach则是指定在运行每一个it体之前需要执行的内容。
expect 和 toEqual,toBeTruthy, toBeDefined, toContain 判断。
所有的测试相关的外部扩展和模拟对象都包含在angular-mocks.js文件里。
对于Angular Service的测试,需要一些初始化工作。
Angular提供了一系列的方法将Jasmine 和依赖注入很好的融合。
angular.module('archive', [])
.factory('notificationsArchive', function () {
var archivedNotifications = [];
return {
archive:function (notification) {
archivedNotifications.push(notification);
},
getArchived:function () {
return archivedNotifications;
}
};
});
测试代码:
describe('notifications archive tests', function () {
var notificationsArchive;
beforeEach(module('archive'));
beforeEach(inject(function (_notificationsArchive_) {
notificationsArchive = _notificationsArchive_;
}));
it('should give access to the archived items', function () {
var notification = {msg: 'Old message.'};
notificationsArchive.archive(notification);
expect(notificationsArchive.getArchived()).toContain(notification);
});
});
这里使用了module和inject函数来初始化加载需要的内容。
module函数时Jasmine中加载某个模块,它类似于 ng-app 指令。告诉AngularJS $injector 应该创建某个模块。
加载模块后,我们就可以通过inject来注入任意的模块定义内容了。
Controller测试:
angular.module('admin-projects', [])
.controller('ProjectsEditCtrl', function($scope, project) {
$scope.project = project;
$scope.removeTeamMember = function(teamMember) {
var idx = $scope.project.teamMembers.indexOf(teamMember);
if(idx >= 0) {
$scope.project.teamMembers.splice(idx, 1);
}
};
//other methods of the controller
});
测试代码:
describe('ProjectsEditCtrl tests', function () {
var $scope;
beforeEach(module('admin-projects'));
beforeEach(inject(function ($rootScope) {
$scope = $rootScope.$new();
}));
it('should remove an existing team member', inject(function($controller) {
var teamMember = {};
$controller('ProjectsEditCtrl', {
$scope: $scope,
project: {
teamMembers: [teamMember]
}
});
//verify the initial setup
expect($scope.project.teamMembers).toEqual([teamMember]);
//execute and verify results
$scope.removeTeamMember(teamMember);
expect($scope.project.teamMembers).toEqual([]);
}));
});
要测试定义在Controller上的方法,我们需要先定义一个新的scope对象,一个controller实例,并把这两者链接起来。
我们还有手动的完成类似ng-controller指令的工作。
我们首先访问$rootScope服务调用其$new来创建一个新的$scope实例($scope.$new())
这是在模拟应用程序开始运行时的工作,此时ng-controller会创建一个新的scope
我们接着调用$controller服务创建一个controller实例,将前面创建的$scope 传递给它。
模拟对象和异步代码测试:
angular.module('async', [])
.factory('asyncGreeter', function ($timeout, $log) {
return {
say:function (name, timeout) {
$timeout(function(){
$log.info("Hello, " + name + "!");
})
}
};
});
$timeout服务是JavaScript的setTimeout函数的替代,延迟执行某些行为。
describe('Async Greeter test', function () {
var asyncGreeter, $timeout, $log;
beforeEach(module('async'));
beforeEach(inject(function (_asyncGreeter_, _$timeout_, _$log_) {
asyncGreeter = _asyncGreeter_;
$timeout = _$timeout_;
$log = _$log_;
}));
it('should greet the async World', function () {
asyncGreeter.say('World', 9999999999999999999);
//
$timeout.flush();
expect($log.info.logs).toContain(['Hello, World!']);
});
});
通过调用$timeout.flush() ,来模拟一次异步事件的触发。
端到端测试,Scenario Runner
通过在真实浏览器内驱动行为来进行集成测试,它自动执行表单填写,单击按钮和链接等行为,检查页面回复,包括页面变化,信息显示等。