目錄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即學即用)