个性化阅读
专注于IT技术分析

第一个AngularJS项目教程第2部分:脚手架,构建和测试工具

本文概述

介绍

有了许多可用于帮助开发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库等。它像rails bundler一样在git上运行, 并且避免了手动下载和更新依赖项的需要。

  • Yeoman是一个包含3个核心组件的工具集:Grunt, Bower和脚手架工具Yo。 Yo在生成器(仅仅是脚手架模板)的帮助下生成样板代码, 并自动为你的项目配置Grunt和Bower。你可以找到几乎所有JavaScript框架(Angular, Backbone, Ember等)的生成器, 但由于我们将重点放在Angular上, 因此我们将使用generator-angular项目。

那么, 我们从哪里开始呢?

好吧, 我们需要做的第一件事就是安装我们需要的工具。

如果尚未安装git, node.js和npm, 请继续安装。

然后, 我们将转到命令行并运行以下命令来安装Yeoman的工具:

npm install -g yo grunt-cli bower

哦, 别忘了, 我们将使用AngularJS生成器, 因此你也需要安装它:

npm install -g generator-angular

好, 现在我们准备好…

脚手架/生成我们的AngularJS应用程序

上一次, 我们从有角种子项目中手动借用了样板代码。这次, 我们让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

瞧!我们的应用程序现在应该在我们面前弹出!

关于Bower for AngularJS的一些知识

在进入真正重要的部分(即测试)之前, 让我们花一点时间来了解有关Bower的更多信息。如前所述, Bower是我们的包裹经理。只需使用bower install命令即可将lib或插件添加到我们的项目中。例如, 要包含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更改为样式, 将js更改为脚本, 等等)。

结果, 我们最初编写的应用程序现在具有与我们刚刚生成的支架不一致的路径。为了解决这个问题, 让我们从此处下载应用程序代码, 然后从那时起使用该版本(它与我们最初编写的应用程序大致相同, 但更新的路径与生成器角度命名相匹配)。

下载应用程序后, 导航到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请求(我们用于从服务器获取信息的请求), 而是返回包含三个驱动程序的静态列表, 模仿真实的服务器响应。这使我们可以肯定地知道应该从控制器返回什么。因此, 我们可以使用Expect()函数将结果与预期结果进行比较。如果它们匹配, 则测试将通过。

只需使用以下命令即可运行测试:

grunt test

驱动程序详细信息控制器(drivercontroller)的测试套件应与我们刚刚看到的套件非常相似。我建议你尝试自己练习一下(或者, 如果不满意, 也可以在这里看看)。

端到端AngularJS测试呢?

Angular团队最近为端到端测试引入了一个名为Protractor的新运行程序。它使用webdriver与浏览器中运行的应用程序进行交互, 并且默认情况下还使用Jasmine测试框架, 因此语法将与我们的单元测试高度一致。

不过, 由于量角器是一种相当新的工具, 因此它与Yeoman堆栈和生成器角度的集成仍然需要大量的配置工作。考虑到这一点, 并且我打算尽可能简化本教程, 因此我的计划是专门写一篇以后的文章, 专门介绍AngularJS中的端到端测试。

总结

在本系列教程的这一点上, 我们已经学习了如何用yo搭建Angular应用程序, 如何使用Bower管理其依赖项以及如何使用业力和量角器编写/运行一些测试。但是请记住, 本教程仅作为对这些AngularJS工具和实践的介绍。我们在这里没有深入分析任何一个。

我们的目标只是帮助你入门。从这里开始, 你可以继续学习有关此惊人框架和工具套件的所有知识。

附录:作者的一些(重要)注释

阅读本教程后, 有些人可能会问:”等等。在开始编写应用程序代码之前, 你不应该做所有这些事情吗?这不应该成为本教程的一部分吗?”

我对此的简短回答是”否”。正如我们在第一部分中所看到的, 你实际上不需要了解所有这些东西即可编写你的第一个Angular应用程序。相反, 我们在本文中讨论的大多数工具旨在帮助你优化开发工作流程并实践测试驱动开发(TDD)。

谈到TDD, TDD的最基本概念当然是一个合理的概念。即, 在编写代码之前先编写测试。但是, 有些人对此概念的看法过高。 TDD是一种发展实践, 而不是一种学习方法。因此, 在编写代码之前先编写测试确实很有意义, 而在学习如何编写代码之前先学习如何编写测试没有意义。

我个人认为, 这是正式的Angular教程可能如此令人费解并且几乎没有以前的前端MVC / TDD经验的人无法理解的主要原因。这就是我开始本教程系列的主要原因之一。

对于那些学习导航AngularJS世界的人来说, 我的个人建议是:不要对自己太苛刻。你无需一次学习所有内容(尽管别人告诉你!)。根据你以前在其他前端/测试框架上的经验, AngularJS最初可能很难理解。因此, 请学习所有需要学习的内容, 直到能够编写自己的简单应用程序为止, 然后, 一旦你熟悉了框架的基础知识, 就可以选择并应用最适合的长期开发实践来关注自己。你。

当然, 这是我的拙见, 并不是每个人都同意这种方法(Angular开发团队可能会在我发布此文章后派遣一名杀手er我), 但这是我的愿景, 并且我敢肯定, 很多人在那里谁会同意我的。

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

赞(0) 打赏
未经允许不得转载:srcmini » 第一个AngularJS项目教程第2部分:脚手架,构建和测试工具
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!

 

觉得文章有用就打赏一下文章作者

微信扫一扫打赏