您的第一個 AngularJS 應用教程第 2 部分:腳手架、構建和測試工具

已發表: 2022-03-11

介紹

由於有許多工具可用於幫助開發 AngularJS 應用程序,許多人認為它是一個極其複雜的框架,但事實並非如此。 這是我開始本教程系列的主要原因之一。

在第一部分中,我們介紹了 AngularJS 框架的基礎知識,並從編寫我們的第一個應用程序開始。 這篇文章是為初學者設計的。 如果您是一位更有經驗的 AngularJS 開發人員,您可能對揭秘指令或 AngularJS 在成長中的初創公司中使用的故事更感興趣。

在本教程中,我們將拋開應用程序邏輯層,學習如何進行正確的 AngularJS 項目設置,包括腳手架、依賴管理以及為測試做準備(包括單元測試和端到端測試)。 我們將使用以下 AngularJS 工具來完成這項工作:Yeoman、Grunt 和 Bower。 然後,我們將回顧使用 Karma 編寫和運行 Jasmine 測試的過程。

Karma、Jasmine、Grunt、Bower、Yeoman……這些工具是什麼?

有很多開發工具可以幫助構建 AngularJS、測試 AngularJS 和正確構建應用程序。

如果您使用 JavaScript,那麼即使您是 Angular 新手,您也很可能至少已經知道其中的一些工具。 但是為了幫助確保一個共同的基線,我將避免做出任何假設。 讓我們簡要回顧一下這些技術及其用途:

  • Karma(以前稱為 Testacular)是 Google 的 JavaScript 測試運行器,也是測試 AngularJS 的自然選擇。 除了允許您在真實瀏覽器(包括手機/平板瀏覽器)上運行測試之外,它還與測試框架無關; 這意味著您可以將它與您選擇的任何測試框架(例如 Jasmine、Mocha 或 QUnit 等)結合使用。

  • Jasmine 將是我們選擇的測試框架,至少在這篇文章中是這樣。 如果您曾經使用過它,它的語法與 RSpec 的語法非常相似。 (如果你還沒有,別擔心;我們將在本教程後面更詳細地檢查它。)

  • Grunt 是一個任務運行器,可幫助自動執行多項重複性任務,例如縮小、編譯(或構建)、測試和設置 AngularJS 應用程序的預覽。

  • Bower 是一個包管理器,可幫助您查找和安裝所有應用程序依賴項,例如 CSS 框架、JavaScript 庫等。 它在 git 上運行,很像 Rails 捆綁器,並且避免了手動下載和更新依賴項的需要。

  • Yeoman 是一個包含 3 個核心組件的工具集:Grunt、Bower 和腳手架工具 Yo。 Yo 在生成器(只是腳手架模板)的幫助下生成樣板代碼,並為您的項目自動配置 Grunt 和 Bower。 您可以找到幾乎所有 JavaScript 框架(Angular、Backbone、Ember 等)的生成器,但由於我們在這里關注 Angular,因此我們將使用生成器 Angular 項目。

那麼,我們從哪裡開始呢?

嗯,我們需要做的第一件事就是安裝我們需要的工具。

如果您還沒有安裝 git、node.js 和 npm,請繼續安裝它們。

然後我們將進入命令行並運行以下命令來安裝 Yeoman 的工具:

 npm install -g yo grunt-cli bower

哦,別忘了,我們將使用 AngularJS 生成器,所以你也需要安裝它:

 npm install -g generator-angular

好的,現在我們準備...

腳手架/生成我們的 AngularJS 應用程序

上次,我們手動從 angular-seed 項目中藉用了樣板代碼。 這一次,我們將讓 yo(與 generator-angular 一起)為我們做這件事。

我們需要做的就是創建我們的新項目文件夾,導航到它並運行:

 yo angular

我們將看到一些選項,例如是否包含 Bootstrap 和 Compass。 現在,讓我們對 Compass說不Bootstrap 說是。 然後,當提示要包含哪些模塊(資源、cookie、清理和路由)時,我們將只選擇angular-route.js

現在應該創建我們的項目腳手架(可能需要一分鐘),與 Karma 集成並進行所有預配置。

注意:請記住,我們將此處的模塊限制為我們在本教程第一部分中構建的應用程序中使用的模塊。 當您為自己的項目執行此操作時,由您決定需要包含哪些模塊。

現在,由於我們將使用 Jasmine,讓我們將karma-jasmine適配器添加到我們的項目中:

 npm install karma-jasmine --save-dev

如果我們希望我們的測試在 Chrome 實例上執行,我們還要添加karma-chrome-launcher

 npm install karma-chrome-launcher --save-dev

好的,如果我們做的一切正確,我們的項目文件樹現在應該如下所示:

使用這些 AngularJS 工具的示例項目文件樹將如下所示。

我們的靜態應用程序代碼進入app/目錄,而test/目錄將包含(是的,你猜對了!)我們的測試。 我們在根目錄上看到的文件就是我們的項目配置文件。 關於它們中的每一個都有很多要學習的東西,但現在我們將堅持使用默認配置。 因此,讓我們第一次運行我們的應用程序,我們只需使用以下命令即可:

 grunt serve

瞧! 我們的應用程序現在應該會出現在我們面前!

關於 AngularJS 的 Bower

在進入真正重要的部分(即測試)之前,讓我們花一點時間了解更多關於 Bower 的信息。 如前所述,Bower 是我們的包管理器。 可以使用bower install命令簡單地向我們的項目添加庫或插件。 例如,要包含modernizr ,我們需要做的就是以下(當然是在我們的項目目錄中):

 bower install modernizr

但是請注意,雖然這確實使modernizr成為我們項目的一部分(它將位於app/bower_components目錄中),但我們仍然負責根據需要將其包含在我們的應用程序中(或管理何時應該包含它)與任何手動添加的庫有關。 一種方法是簡單地將以下<script>標籤添加到我們的index.html

 <script src="bower_components/modernizr/modernizr.js"></script>

或者,我們可以使用bower.json文件來管理我們的依賴項。 直到現在仔細執行每一步之後, bower.json文件應該如下所示:

 { "name": "F1FeederApp", "version": "0.0.0", "dependencies": { "angular": "1.2.15", "json3": "~3.2.6", "es5-shim": "~2.1.0", "jquery": "~1.11.0", "bootstrap": "~3.0.3", "angular-route": "1.2.15" }, "devDependencies": { "angular-mocks": "1.2.15", "angular-scenario": "1.2.15" } }

語法是不言自明的,但可以在此處獲得更多信息。

然後我們可以添加我們想要的任何其他新依賴項,然後我們只需要以下命令來安裝它們:

 bower install

現在讓我們編寫一些測試!

好的,現在是時候從我們在第一部分中斷的地方開始,為我們的 AngularJS 應用程序編寫一些測試了。

但首先,我們需要解決一個小問題:雖然 generator-angular 的開發人員基於 angular-seed 項目(這是官方 Angular 樣板)的項目模板,但出於某種我不太明白的原因,他們決定更改app文件夾命名約定(將css更改為styles ,將js更改為scripts ,等等)。

結果,我們最初編寫的應用程序現在的路徑與我們剛剛生成的腳手架不一致。 為了解決這個問題,讓我們從這裡下載應用程序代碼並從現在開始使用該版本(它與我們最初編寫的應用程序幾乎完全相同,但更新了路徑以匹配生成器角度命名)。

下載應用程序後,導航到tests/spec/controllers文件夾並創建一個名為drivers.js的文件,其中包含以下內容:

 describe('Controller: driversController', function () { // First, we load the app's module beforeEach(module('F1FeederApp')); // Then we create some variables we're going to use var driversController, scope; beforeEach(inject(function ($controller, $rootScope, $httpBackend) { // Here, we create a mock scope variable, to replace the actual $scope variable // the controller would take as parameter scope = $rootScope.$new(); // Then we create an $httpBackend instance. I'll talk about it below. httpMock = $httpBackend; // Here, we set the httpBackend standard reponse to the URL the controller is // supposed to retrieve from the API httpMock.expectJSONP( "http://ergast.com/api/f1/2013/driverStandings.json?callback=JSON_CALLBACK").respond( {"MRData": {"StandingsTable": {"StandingsLists" : [{"DriverStandings":[ { "Driver": { "givenName": 'Sebastian', "familyName": 'Vettel' }, "points": "397", "nationality": "German", "Constructors": [ {"name": "Red Bull"} ] }, { "Driver": { "givenName": 'Fernando', "familyName": 'Alonso' }, "points": "242", "nationality": "Spanish", "Constructors": [ {"name": "Ferrari"} ] }, { "Driver": { "givenName": 'Mark', "familyName": 'Webber' }, "points": "199", "nationality": "Australian", "Constructors": [ {"name": "Red Bull"} ] } ]}]}}} ); // Here, we actually initialize our controller, passing our new mock scope as parameter driversController = $controller('driversController', { $scope: scope }); // Then we flush the httpBackend to resolve the fake http call httpMock.flush(); })); // Now, for the actual test, let's check if the driversList is actually retrieving // the mock driver array it('should return a list with three drivers', function () { expect(scope.driversList.length).toBe(3); }); // Let's also make a second test checking if the drivers attributes match against // the expected values it('should retrieve the family names of the drivers', function () { expect(scope.driversList[0].Driver.familyName).toBe("Vettel"); expect(scope.driversList[1].Driver.familyName).toBe("Alonso"); expect(scope.driversList[2].Driver.familyName).toBe("Webber"); }); });

這是我們的driverscontroller的測試套件。 它可能看起來很多代碼,但實際上大部分只是模擬數據聲明。 讓我們快速瀏覽一下真正重要的元素:

  • describe()方法定義了我們的測試套件。
  • 每個it()都是正確的測試規範。
  • 每個beforeEach()函數都在每個測試之前執行。

這裡最重要(並且可能令人困惑)的元素是我們在httpMock變量上實例化的$httpBackend服務。 該服務充當假後端,並在測試運行時響應我們的 API 調用,就像我們的實際服務器在生產中所做的那樣。 在這種情況下,使用expectJSONP()函數,我們將其設置為攔截對給定 URL 的任何 JSONP 請求(與我們用來從服務器獲取信息相同的 URL),而是返回一個包含三個驅動程序的靜態列表,模仿真實的服務器響應。 這使我們能夠確定應該從控制器返回什麼。 因此,我們可以使用expect()函數將結果與預期結果進行比較。 如果它們匹配,則測試將通過。

只需使用以下命令即可運行測試:

 grunt test

驅動程序詳細信息控制器 ( drivercontroller ) 的測試套件應該與我們剛剛看到的非常相似。 我建議您嘗試自己將其作為練習來解決(或者,如果您不適應,也可以在這裡查看)。

那麼端到端 AngularJS 測試呢?

Angular 團隊最近推出了一種名為 Protractor 的用於端到端測試的新運行程序。 它使用 webdriver 與運行在瀏覽器中的應用程序進行交互,並且默認使用 Jasmine 測試框架,因此語法將與我們的單元測試高度一致。

然而,由於 Protractor 是一個相當新的工具,它與 Yeoman 堆棧和generator-angular的集成仍然需要大量的配置工作。 考慮到這一點,並且我打算使本教程盡可能簡單,我的計劃是在未來專門發表一篇文章,專門介紹 AngularJS 中的端到端測試。

結論

在本教程系列的這一點上,我們已經學習瞭如何使用yo搭建我們的 Angular 應用程序,使用bower管理它的依賴項,以及使用karmaprotractor編寫/運行一些測試。 但請記住,本教程僅作為對這些 AngularJS 工具和實踐的介紹; 我們沒有在這裡深入分析它們中的任何一個。

我們的目標只是幫助您開始這條道路。 從這裡開始,您可以繼續學習這個驚人的框架和工具套件。

附錄:作者的一些(重要)註釋

讀完本教程後,有些人可能會問: “等等。 在真正開始編寫應用程序之前,您不應該做所有這些事情嗎? 這不應該是本教程的一部分嗎?”

我對此的簡短回答是否定的。 正如我們在第一部分中看到的那樣,您實際上不需要知道所有這些東西來編寫您的第一個 Angular 應用程序。 相反,我們在本文中討論的大多數工具旨在幫助您優化開發工作流程和實踐測試驅動開發 (TDD)。

而說到TDD,TDD最基本的概念當然是一個健全的概念; 即,在編寫代碼之前編寫測試。 不過,有些人認為這個概念太過分了。 TDD 是一種開發實踐,而不是一種學習方法。 因此,在編寫代碼之前編寫測試確實很有意義,而在學習如何編碼之前學習如何編寫測試則沒有。

我個人認為這就是為什麼官方 Angular 教程可能感覺如此復雜,並且對於以前沒有前端 MVC/TDD 經驗的人來說幾乎不可能遵循的主要原因。 這是我開始本教程系列的主要原因之一。

我對那些學習駕馭 AngularJS 世界的人的個人建議是:不要對自己太苛刻。 您不需要一次全部學習所有內容(儘管人們告訴您否則!)。 根據您之前使用其他前端/測試框架的經驗,AngularJS 一開始可能很難理解。 所以學習所有你需要學習的東西,直到你能夠編寫自己的簡單應用程序,然後,一旦你對框架的基礎知識感到滿意,你就可以考慮選擇和應用最適合的長期開發實踐你。

當然,這是我的拙見,並不是每個人都會同意這種方法(一旦我發表這篇文章,Angular 開發團隊可能會派一個僱傭殺手來跟踪我),但這是我的願景,我很確定有很多人外面誰會同意我的看法。

相關: Angular 6 教程:具有新功能的新功能