Учебное пособие по вашему первому приложению AngularJS, часть 2: инструменты для создания шаблонов, сборки и тестирования
Опубликовано: 2022-03-11Введение
Благодаря множеству доступных инструментов, помогающих в разработке приложений AngularJS, у многих создается впечатление, что это чрезвычайно сложная структура, что совсем не так. Это одна из основных причин, по которой я начал эту серию руководств.
В первой части мы рассмотрели основы фреймворка AngularJS и начали с написания нашего первого приложения. Этот пост рассчитан на новичков. Если вы более опытный разработчик AngularJS, вас может заинтересовать демистификация директив или история использования AngularJS в растущем стартапе.
В этом руководстве мы отложим в сторону уровень логики приложения и узнаем, как выполнить правильную настройку проекта AngularJS, включая формирование шаблонов, управление зависимостями и подготовку его к тестированию (как модульному, так и сквозному). Мы сделаем это, используя следующие инструменты AngularJS: Yeoman, Grunt и Bower. Затем мы рассмотрим процесс написания и запуска тестов Jasmine с помощью Karma.
Karma, Jasmine, Grunt, Bower, Yeoman… Что это за инструменты?
Если вы работаете с JavaScript, весьма вероятно, что вы уже знакомы по крайней мере с некоторыми из этих инструментов, даже если вы новичок в Angular. Но чтобы помочь обеспечить общую основу, я буду избегать каких-либо предположений. Давайте кратко рассмотрим каждую из этих технологий и то, для чего они полезны:
Karma (ранее известная как Testacular) — это средство запуска тестов JavaScript от Google и естественный выбор для тестирования 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, мы собираемся использовать проект генератор-угловой.
Итак, с чего начнем?
Ну, первое, что нам нужно сделать, это установить инструменты, которые нам понадобятся.
Если у вас еще не установлены git, node.js и npm, установите их.
Затем мы перейдем в командную строку и выполним следующую команду для установки инструментов Yeoman:
npm install -g yo grunt-cli bower
Да, и не забывайте, мы собираемся использовать генератор AngularJS, поэтому вам также необходимо установить его:
npm install -g generator-angular
Хорошо, теперь мы готовы…
Скаффолд/генерация нашего приложения AngularJS
В прошлый раз мы вручную позаимствовали наш шаблонный код из проекта angular-seed. На этот раз мы позволим yo (вместе с генератором-угловым) сделать это за нас.
Все, что нам нужно сделать, это создать новую папку проекта, перейти к ней и запустить:
yo angular
Нам будут предложены некоторые варианты, например, включать ли Bootstrap и Compass. А пока скажем « нет » Compass и « да » Bootstrap. Затем, когда нас спросят о том, какие модули включить (ресурс, файлы cookie, sanitize и route), мы выберем только angular-route.js
.
Наш каркас проекта теперь должен быть создан (это может занять минуту), интегрирован с Karma и предварительно настроен.
Примечание. Имейте в виду, что мы ограничиваем модули здесь теми, которые мы использовали в приложении, которое мы создали в первой части этого руководства. Когда вы делаете это для своего собственного проекта, вам решать, какие модули вам нужно будет включить.
Теперь, поскольку мы собираемся использовать Jasmine, давайте добавим в наш проект адаптер karma-jasmine
:
npm install karma-jasmine --save-dev
Если мы хотим, чтобы наши тесты выполнялись на экземпляре Chrome, давайте также добавим karma-chrome-launcher
:
npm install karma-chrome-launcher --save-dev
Хорошо, если мы все сделали правильно, дерево файлов нашего проекта теперь должно выглядеть так:
Код нашего статического приложения помещается в каталог app/
а каталог test/
будет содержать (да, вы уже догадались!) наши тесты. Файлы, которые мы видим в корне, являются файлами конфигурации нашего проекта. О каждом из них нужно многое узнать, но пока мы просто будем придерживаться конфигурации по умолчанию. Итак, давайте запустим наше приложение в первый раз, что мы можем сделать просто с помощью следующей команды:
grunt serve
И вуаля! Теперь наше приложение должно появиться перед нами!
Немного о Bower для AngularJS
Прежде чем перейти к действительно важной части (т. е. к тестированию), давайте на минутку узнаем немного больше о 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.
Но сначала нам нужно решить небольшую проблему: хотя разработчики генератора-углового основывали свой шаблон проекта на проекте-семени (который является официальным шаблоном 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()
выполняется непосредственно перед каждым из тестов.
Наиболее важным (и потенциально запутанным) элементом здесь является служба $httpBackend
, экземпляр которой мы создали в переменной httpMock
. Этот сервис действует как фальшивый бэкенд и отвечает на вызовы нашего API во время тестовых прогонов точно так же, как наш настоящий сервер в продакшене. В этом случае, используя expectJSONP()
, мы настраиваем ее на перехват всех запросов JSONP к заданному URL-адресу (тому же, который мы используем для получения информации с сервера) и вместо этого возвращаем статический список с тремя драйверами, имитируя реальный ответ сервера. Это позволяет нам точно знать, что должно вернуться от контроллера. Поэтому мы можем сравнить результаты с ожидаемыми, используя функцию expect()
. Если они совпадают, тест пройден.
Запуск тестов просто выполняется с помощью команды:
grunt test
Набор тестов для контроллера сведений о драйвере ( drivercontroller
) должен быть очень похож на тот, который мы только что видели. Я рекомендую вам попытаться понять это самостоятельно в качестве упражнения (или вы можете просто посмотреть здесь, если вы не в состоянии).
Как насчет сквозных тестов AngularJS?
Команда Angular недавно представила новый бегун для сквозных тестов под названием Protractor. Он использует веб-драйвер для взаимодействия с приложением, работающим в браузере, а также по умолчанию использует среду тестирования Jasmine, поэтому синтаксис будет полностью соответствовать синтаксису наших модульных тестов.
Однако, поскольку Protractor — довольно новый инструмент, его интеграция со стеком Yeoman и generator-angular
по-прежнему требует значительной работы по настройке. Имея это в виду, и мое намерение сделать это руководство как можно более простым, я планирую посвятить будущий пост исключительно подробному освещению сквозного тестирования в AngularJS.
Заключение
На этом этапе серии руководств мы научились формировать наше приложение Angular с помощью yo
, управлять его зависимостями с помощью bower
и писать/выполнять некоторые тесты с использованием karma
и protractor
. Имейте в виду, однако, что это руководство предназначено только для ознакомления с этими инструментами и практиками AngularJS; мы не анализировали ни один из них здесь очень глубоко.
Нашей целью было просто помочь вам начать этот путь. Отсюда вы можете продолжить и узнать все, что можете, об этой удивительной структуре и наборе инструментов.
Приложение: Некоторые (важные) примечания от автора
После прочтения этого руководства некоторые люди могут спросить: «Подождите. Разве вы не должны делать все это до того, как на самом деле начнете кодировать свое приложение? Разве это не должно было быть первой частью этого урока?»
Мой короткий ответ на это — нет . Как мы видели в первой части, вам на самом деле не нужно знать все эти вещи, чтобы написать свое первое приложение Angular. Скорее, большинство инструментов, которые мы обсуждали в этом посте, предназначены для того, чтобы помочь вам оптимизировать рабочий процесс разработки и практиковать разработку через тестирование (TDD).
И если говорить о TDD, самая основная концепция TDD, безусловно, здравая; а именно, напишите свои тесты до того, как вы напишете свой код. Однако некоторые люди слишком далеко заходят в этой концепции. TDD — это практика разработки, а не метод обучения. Соответственно, написание тестов до написания кода имеет большой смысл, в то время как научиться писать тесты до того, как научиться программировать, не имеет смысла.
Лично я считаю, что это основная причина, по которой официальные руководства по Angular могут казаться такими запутанными и почти невозможными для понимания людьми, не имевшими опыта работы с интерфейсом MVC/TDD. Это одна из основных причин, почему я начал эту серию руководств.
Мой личный совет для тех, кто учится ориентироваться в мире AngularJS: не будьте слишком строги к себе. Вам не нужно изучать все сразу (несмотря на то, что люди говорят вам обратное!). В зависимости от вашего предыдущего опыта работы с другими интерфейсными/тестирующими средами, AngularJS может быть довольно сложным для понимания на начальном этапе. Так что изучайте все, что вам нужно, пока вы не сможете писать свои собственные простые приложения, а затем, как только вы освоитесь с основами фреймворка, вы можете заняться выбором и применением долгосрочных методов разработки, которые лучше всего подходят для ты.
Конечно, это мое скромное мнение, и не все согласятся с таким подходом (и команда разработчиков Angular может послать за мной наемного убийцу, как только я опубликую это), но это мое видение, и я уверен, что многие люди там, кто согласится со мной.