目录javascript
AngularJS框架的核心概念是MVC架构模式(或者说MVVM,Model-View-ViewModel,这两个模式差异不大)。MVC架构模式能够讲整个应用划分红三个彻底不相关的独立模块:
(1)模型Model:是整个应用的驱动力。通常来讲,指的是应用从服务器端获取的数据,任何在UI上看到的数据都是从模型或模型的子集中获取的。
(2)视图View:是用户能够浏览并与之交互的UI界面。它是动态的,基于当前系统的模型。
(3)控制器Controller:表明业务逻辑和表现层。控制器负责具体实现方式来决定将哪些模型展示在视图中,所以它能够被看做是一个视图模型,或者展示器(presenter)。html
这样将应用分拆成独立的子单元的好处是:
(1)每一个单元只负责作一件事情,符合单一职责原则。模型层负责数据操做,视图层展示UI界面,控制器负责业务逻辑。
(2)每一个单元之间相互独立,这使得模块化、可重用性和可维护性大大提升。java
一种情形:jquery
HTML:Hello <span id="name"></span> Javascript: var updateNameUI = function(name){ $('#name').text(name); } // 首次加载数据时 updateNameUI(user.name); // 当数据变化时从新显示 updateNameUI(updatedName);
如上,显示数据须要找到对应的UI元素并更新它的innerText,每次名称变化,都不得不调用以此该函数。而AngularJS采用模型驱动应用,经过数据绑定来实现,既有单向绑定,也有双向绑定。angularjs
把数据绑定到HTML上,AngularJS会负责正确的将数据传递给UI,一旦数据变化,AngularJS会检查到变化并自动更新UI。单向绑定的实现方式:Hello <span ng-bind="name"></span>
或Hello <span>{ {name} }</span>
web
另外一种情形:正则表达式
<form name="myForm" onsubmit="submitData()"> <input type="text" id="nameField" /> <input type="text" id="emailField" /> </form>
function setUserDetails(userDetails){ $('#nameField').value(userDetails.name); $('#emailField').value(userDetails.email); } function getUserDetails(){ return { name: $('#nameFeild').value(), email: $('#emailFeild').value() } } var submitData = function(){ makeXhrRequest('http://url', getUserDetails); }
上面这种情形,除了页面布局和模板以外,还须要编写代码来控制业务逻辑/控制器与UI之间的数据双向传递,须要本身手动更新数据和获取数据。AngularJS提供了双向数据绑定,不须要再编写额外的代码区传递数据。双向数据绑定可让控制器和UI共享一个数据模型,任何一边修改了数据,都会致使另外一边自动更新。实现方式:chrome
<form name="myForm" onsubmit="submitData()"> <input type="text" ng-model="uer.name" /> <input type="text" ng-model="user.email" /> </form>
未经扩展的原生HTML模板很难体现出页面的构成,好比下面的结构:npm
<ul calss="nav nav-tabs"> <li>Home</li> <li class="selected">Profile</li> </ul> <div class="tab1">Some Content Here</div> <div class="tab2"><input id="startDate" type="text" /></div>
AngularJS定义了声明范式,直接在HTML里声明你想要什么就能够了。AngularJS经过声明实现上述功能,加强HTML,以下代码(IE8及如下版本不支持扩展HTML标签):json
<tabs> <tab title="Home">Some Content Here</tab> <tab title="Profile"><input type="text" datepicker ng-model="startDate" /></tab> </tabs>
这样的作法的好处是:
AngularJS的全部应用均可以归结于MVC架构:
依赖注入是指当咱们须要某个具体的控制器或者服务时,并不用直接在代码中用new操做符或函数显示建立实例(相似DatabaseFactory.getInstance()),而是发送请求以获取它的全部依赖关系。
这样作的好处是咱们并不须要关心如何构建这些依赖关系以及在开始以前就明确咱们须要什么。
如指令,极大地扩展了浏览器和HTML的功能。
控制器、服务、过滤器、指令
AngularJS从控制器、服务、指令到视图、页面迁移都被设计成可测试性的。AngularJS中的控制器和视图是相互独立的,并且依赖注入的部分一样具备高可测试性。Karma提供了单元测试环境,Protractor是一个基于WebDriver的端到端测试环境。
在百度百科上的解释:
单元unit:单元就是相对独立的功能模块。一个完整的、模块化的程序都是由许多单元构成,单元完成本身的任务、而后与其余单元进行交互,最终协同完成整个程序的功能。
测试test:测试就是判断测试对象对于某个特定的输入有没有预期的输出。
工程上的一个共识是:若是程序的每一个模块都是正确的,模块与模块之间的链接是正确的,那么程序基本上就是正确的。
因此单元测试就是一种保证构成程序的每一个模块的正确性,从而保证整个程序的正确性的方法论。单元测试(优先)的目的就是首先保证一个系统的基本组成单元、模块(如对象以及对象中的方法)能正常工做,这是一种分而治之中的bottom-up思想。
因此,为何须要单元测试?
基本思路是经过测试来推进整个开发的进行。原理是在开发功能代码以前,先编写单元测试用例代码,经过测试代码来肯定须要编写什么产品代码。因此测试驱动开发不只仅是将测试当成验证的工具,而是把需求分析、设计、质量控制量化的过程。
TDD的测试步骤是:先写测试——再写代码——测试——重构——经过
BDD是TDD的进化,但关注的课核心是设计,在行为驱动开发中,定义系统的行为(由客户和开发者一块儿定义系统的行为,避免表达不一致带来的问题)是主要工做,而对系统的描述则成了测试标准。
一款BDD模式的测试框架:Jasmine
"Jasmine is a Behavior-Driven Development testing framework for JavaScript. It does not rely on browsers, DOM, or any JavaScript framework.Thus it's suited for websites, Node.js projects, or anywhere that JavaScript can run." —— 来自官网
以最简单的运算类函数为例,通常能覆盖如下几种值就好了:
Karma是测试运行器,它只负责找出代码中全部的单元测试用例,而后打开浏览器并测试它们,最终获取测试结果。并不关心那些测试用例究竟是用什么语言编写的,以及咱们究竟采用的是什么框架,它所作的仅仅是运行这些测试而已。
Jasmine是一种测试框架,它定义了测试用例的语法和API,以及如何为这些用例编写断言。它还有其余的替代品,如mocha、qunit等。
Karma经过NodeJS和SocketIO来进行测试,适用于不一样的浏览器,速度很快。
如Chrome插件karma-chrome-launcher,也有对应的Firefox、IE等浏览器的插件。
能够选择采用哪一种框架来编写但与测试,如Jasmine的插件karma-jasmine,也有其余风格的框架的插件,如mocha、qunit。
Karma的测试结果提供了丰富的格式,默认的报表生成器是内置的,报表插件如karma-html-reporter,karma-junit-reporter。
它们可以和已有的JS库或者工具进行整合,Karma插件涵盖了大部分主流的JS库。
npm init
推荐为每一个项目本地安装Karma,而不是安装一个全局的Karma。
npm install karma -g npm install karma-jasmine -g npm install karma-chrome-launcher -g npm install karma-cli -g npm install karma-coverage -g npm install karma-html-reporter -g npm install karma-junit-reporter -g (jasmine-core)
(将上面的参数 -g 替换成 --save-dev 会在该项目内安装,而不是在全局安装,而且在 package.json 中 dev-dependencies 中显示依赖的相关包)
karma 提供了自动生成配置文件的方法。执行 karma init,按照提示回答几个问题便可,默认文件名 karma.config.js。
// 文件 karma.config.js // Karma configuration // Generated on Tue Nov 01 2016 14:17:00 GMT+0800 (中国标准时间) module.exports = function(config) { config.set({ // base path that will be used to resolve all patterns (eg. files, exclude) // 放置文件的根目录 basePath: '', // available frameworks: https://npmjs.org/browse/keyword/karma-adapter // 使用哪些测试框架(jasmine/mocha/qunit/...),如Jasmine,需安装karma-jasmine插件 frameworks: ['jasmine'], // list of files / patterns to load in the browser // 浏览器须要加载的文件列表或者文件匹配表达式 // 须要加载入浏览器的js文件,包括基础的类库,被测试js源文件和测试用例js文件 // 若是须要测试angular代码,好比引入angular-mock.js,给angular代码进行mock files: [ '../web/vender/jquery/jquery-1.10.2.min.js', '../web/vender/angular/angular.min.js', '../web/vender/angular/angular-ui-router.min.js', '../web/vender/bootstrap_v3.3.5/js/bootstrap.min.js', 'lib/angular-mocks.js', // 注意angular-mock的版本必定要和angular版本一致 '../web/common/*.js', /****/ '../web/bbs/template/*.html', '../web/common/template/**/*.html', /***/ 'tc/ut/bbs/*.js' ], // list of files to exclude 须要排除的文件列表或者文件匹配正则表达式 exclude: [ // 'tc/ut/apply/apply.js', 'tc/ut/project/my/task.js', ], // test results reporter to use 这里定义输出的报告 // possible values: 'dots', 'progress' // available reporters: https://npmjs.org/browse/keyword/karma-reporter // html对应karma-html-reporter组件,coverage对应karma-coverage组件,输出测试用例执行报告 reporters: ['progress', 'html', 'junit','coverage'], junitReporter: { // will be resolved to basePath (in the same way as files/exclude patterns) outputFile: 'report/ut/test-results.xml', suite: 'UT', useBrowserName: false }, htmlReporter: { outputDir: 'report/ut', reportName: 'result' // outputDir+reportName组成完整的输出报告格式,如没有定义,会自动生成浏览器+OS信息的文件夹,不方便读取报告 }, //定义须要统计覆盖率的文件 preprocessors: { '../web/bbs/bbs.js':'coverage', '../web/common/*.js':'coverage', '../web/bbs/**/*.html': 'ng-html2js', '../web/common/template/*.html': 'ng-html2js' }, coverageReporter: { type: 'cobertura', //'cobertura', //将覆盖率报告类型type设置为html subdir:'coverage', //dir+subdir组成完整的输出报告格式,如没有定义,会自动生成浏览器+OS信息的文件夹,不方便读取报告 dir: 'report/ut/' //代码覆盖率报告生成地址 }, // web server port port: 9876, // enable / disable colors in the output (reporters and logs) colors: true, // level of logging 日志等级 // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG logLevel: config.LOG_INFO, // enable / disable watching file and executing tests whenever any file changes // 进行测试时是否容许随时监视文件变化(被测试文件和测试用用例文件),若有修改,自动从新执行测试 // 不然用户就得手动在终端中运行 karma run 进行另外一轮测试,此命令可让karma以当前的服务器配置再次进行相同的测试 autoWatch: true, // Continuous Integration mode 持续集成模式(是否重复运行) // if true, Karma captures browsers, runs the tests and exits // 若是设置为true,则会启动浏览器,运行测试而后退出。在持续集成的环境下,该参数应该被设置为true // 上一个参数为true,本参数为false,则自动监视才生效。不然执行完测试用例后自动退出 singleRun: false, // start these browsers // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher // 用来执行自动监听的浏览器,推荐chrome browsers: ['Chrome'], // Concurrency level // how many browser should be started simultaneous concurrency: Infinity, /*captureTimeout : 60000, browserDisconnectTimeout : 10000, // default 2000 browserDisconnectTolerance : 1, // default 0 browserNoActivityTimeout : 60000, //default 10000*/ ngHtml2JsPreprocessor: { cacheIdFromPath: function(filepath) { var cacheId = filepath.substr(filepath.lastIndexOf('/webapp/')+7); // console.log(cacheId); return cacheId; }, moduleName: 'template' } }); };
运行 karma start,会在运行目录中搜索karma.conf.js文件并加载配置内容。若是配置文件的名称不是这个名字,或者在不一样的目录中,能够讲完整路径做为参数传入:karma start [my.karma.conf.js]
安装karma-coverage插件:
npm install karma-coverage --save-dev
修改karma.config.js, 增长覆盖率配置:
// 定义须要统计覆盖率的文件 preprocessors: { 'src/**/*.js': ['coverage'] }, reporters: ['progress', 'html', 'junit', 'coverage'], //在 reporters 中增长 coverage coverageReporter: { type: 'html', // 将覆盖率报告类型type设置为html // 'cobertura' subdir:'coverage', // dir+subdir 组成完整的输出报告格式,如没有定义,会自动生成浏览器+OS信息的文件夹,不方便读取报告 dir: 'report/ut/' // 代码覆盖率报告生成地址 }
Jasmine框架是一种使用行为驱动的方式来构建测试的。具体说就是描述想要的行为并设定预期。
它的大部分的测试代码demo 参见 【AngularJS的概念及其单元测试】之Jasmine测试脚本Demo
AngularJS:Up & Running (AngularJS即学即用)