Статьи

AngularJS для повстанцев: более низкий стиль технологии для предприятия

Я играл с использованием углового , который не соответствует рекомендованному Grunt / Бауэр / НПМ передовой практика инструмента цепи. Гипотеза состоит в том, что многие предприятия Java не готовы идти по этому пути. Может случиться так, что разработчикам будет удобнее использовать Java IDE и инструменты сборки. Вполне возможно, что специалисты по инфраструктуре не готовы обновить инструменты CI, чтобы иметь возможность работать с Grunt и всем этим. Я также пытаюсь понять, есть ли более быстрый способ создания Angular-приложений, будучи частью команды, которая работала быстрее со старыми (худшими) технологиями .

В этой статье я опишу уровень выше службы бронирования greyhound.com:

AngularJS с меньшим количеством JavaScript

Меньше JavaScript был одной из целей эксперимента. Если нам по-прежнему нужно ориентироваться на Internet Explorer 7 и 8, нам нужно беспокоиться о том, чтобы перегружать эти браузеры. По крайней мере, мы должны с энтузиазмом проверять каждую неделю проекта, который должен его поддерживать. Мы также должны убедиться, что открытый исходный код, который мы используем, также поддерживает его. Я уже потерпел неудачу в этом отношении с помощью AngularUI, так как он не поддерживает IE7. Хорошо, что угодно, давайте продолжим со статьей.

Чтобы достичь цели «меньше JavaScript», я старался меньше использовать рекомендованные Angular-командой сервисы, директивы и модули. Вместо этого я придерживаюсь одного контроллера JavaScript с одним списком инъекционных сервисов. Не волнуйтесь, я все еще делю разделение проблем во время разработки. Я также избегаю Жасмин, что является, пожалуй, самым противоречивым аспектом того, что я здесь демонстрирую. Наконец, я не использую карму (или транспортир) для сквозного тестирования. Вместо этого я буду тестировать на Java и использовать WebDriver (и некоторые библиотеки) в качестве механизма для тестирования всего HTML, JavaScript и подмножества самого Java-бэкенда. См. Проверка компонентов ниже.

Я использую TestNG, но JUnit будет просто отличной альтернативой. Это также может быть .Net & NUnit или Ruby & RSpec.

Билеты на автобусы, заимствуя услуги Greyhound.com

Я позаимствовал приложение «Web 1.5», размещенное на www.greyhound.com. В настоящее время он написан на .Net и использует коммерческие средства управления для некоторых тяжелых работ (Telerik). Я поставил только два компонента — критерии поиска и результаты поиска. Так как это совершенно не одобрено использованием их услуг, вы не можете купить билеты. Я думаю, если бы я зашел так далеко, меня бы навестили в полиции. Борзая может легко отключить «поддержку» для этого доказательства концепции, проверив реферрера на входящие запросы на обслуживание.

Run-Time Technologies

Это не сильная рекомендация, я просто решил поддержать идею тестирования компонентов в браузере для гипотетической корпоративной команды разработчиков. Упражнение можно было бы так же легко выполнить в .Net или Ruby.

сервер

клиент

Технологии времени сборки

Настройка моей Dev Machine

Я установил инструмент DNS (dnsmasq) на мой Mac. Это позволяет мне отображать все * .dev на 127.0.0.1. Это позволяет мне открыть одну страницу в Firefox (WebDriver) и иметь другой домен для каждого метода тестирования, но при этом оставить открытым одно и то же окно браузера. Мне больше не нужно беспокоиться о состоянии DOM из одного теста, просачивающегося в другой. У меня было это с изменением портов в более ранней версии, но фигурные домены были лучшим решением. Вот запись в блоге, которая ускорила меня через настройку для него . В видео тестов пользовательского интерфейса вы можете видеть, как меняется доменное имя в зависимости от теста.

Кроме этого, я использую Intellij IDEA от Jetbrains — Rolls Royce из IDE уже 13 лет.

Maven Приобретенные зависимости:

  • FluentSelenium & ngWebDriver (я веду тех)

    • Selenium-WebDriver (транзитивно)
  • Moco ( отмечен наградами и коллегой ThoughtWorks Чжэн Е). Я не использую выпущенную версию. Я использую невыпущенную (но самосборную) версию — извините!
  • TestNGGuice для внедрения зависимостей)
  • Hamcrest для утверждений
  • Decdnorator — сшивает фрагменты HTML / JavaScript на страницы (снова я)
  • Maven (хотя может быть так же легко Gradle).

Примечание: Moco (как уже упоминалось) нужно будет клонировать и собрать из root (с Gradle), если вы хотите запускать тесты самостоятельно. В частности, проверьте версию 771a4e84b3e9f129391d45a854c60906d0192ee4 и соберите ее. Как только 1 ноября будет выпущен релиз , я перейду к выпущенному артефакту.

Selenium-WebDriver как костыль

Это было верно для Selenium 1.0, и это все еще классические ошибки с WebDriver (Selenium 2.0):

1. Пройдя длинный путь, чтобы добраться до тестируемого компонента (в частности, CUT) — в частности, войти в систему и начальные целевые страницы.
2. Быть подключенным к полному стеку во время теста, включая известные исходные позиции данных, которые требуют времени готовности
3 Наличие хрупких тестов из-за тонкостей, непоследовательности времени или, в частности, № 3.
4. Зависит от того, чтобы Selenium покрывал слишком большую часть вашего приложения, и, следовательно, занимал слишком много общей продолжительности сборки.

Команды разработчиков всегда заканчивают тем, что сожалели о слишком большом использовании Selenium.

развитие

Мой репо для этого https://github.com/paul-hammant/greyangular . Это работоспособный и работоспособный (после сборки Moco выше — извините).

Компонент Страницы

Основной целью было создать рабочую HTML-страницу только для каждого компонента. Не только это, но вы должны иметь возможность перейти непосредственно к каждому, и не интегрироваться ни с какими другими сервисами из «prod» кодовой базы (по крайней мере, во время автоматизации тестирования). Действительно, тестирование и тестируемость являются первой прерогативой дизайна, но в рамках ограничения не пишите больше JavaScript, который вам нужен.

Вы можете попробовать страницы компонента с реальными / живыми сервисами, например так:

mvn jetty:run

Критерий поиска

Обратите внимание на URL-адрес, который предназначен только для этого компонента, и отладку на странице, которую Decdnorator будет извлекать, когда готовит конечный продукт.

Нажав «Поиск», вы перейдете к отдельному компоненту SearchResults.html.

результаты поиска

Параметры критерия поиска находятся справа от # в URL. В стеке Java фактический поиск выполняется в связи с этим компонентом.

Нажмите «Показать расписание», чтобы перейти к отдельному компоненту ShowSchedule.html.

Показать расписание

Расписание для одного маршрута результатов поиска. Поиск должен был произойти первым, чтобы быть на компоненте.

Тестирование компонентов

Каждый из этих трех компонентов имеет тестовый класс TestNG. Тестовый класс тестирует положительные пути, пути ошибок и исключения для компонента. Класс TestNG создает экземпляр WebDriver (вместе с FluentSelenium) и открывает страницу. Страница переходит к реальным CDN для битов и кусочков Angular, AngularUI и Bootstrap, но использует нереальные сервисы через Moco. Другими словами, тесты все равно пройдут, если Greyhound.com не работает (это было однажды во время моей разработки).

Вот что я тестирую, используя браузер, чтобы охватить все пути JS + Angular-HTML, которые могут быть запущены:

  • Критерий поиска

    • взаимодействие даты и времени меняет модель поиска
    • нажатие кнопки поиска запускается через проверки
    • взаимодействие поиска отправителя и получателя изменяет модель поиска
  • Компонент результатов поиска

    • фиктивные параметры вынуждают вернуться на страницу критериев
    • пропущенные параметры вынуждают вернуться на страницу критериев
    • нет результатов поиска показывает подходящую ошибку
    • индикатор выполнения появляется перед результатами
    • для результатов поиска щелчок выбора действует как переключатель
  • Показать расписание компонента

    • расписание может быть показано
    • нет расписания показывает подходящую ошибку

Приложение, которое создается и тестируется ( видео )

Модульные, интеграционные и компонентные тесты

Весь тестовый набор занимает 33 секунды, включая весь материал Maven. Через пять секунд WebDriver открывает Firefox (хотелось бы, чтобы был предварительно открыт). Во всяком случае, 33 секунд, как я говорю:

mvn clean install

Модульные тесты Java занимают 5 секунд, но их не так много. Аналогично интеграция — 10 секунд, а компонента (через Selenium / WebDriver) — 21 секунда.

Вы можете запустить такие, если хотите:

mvn clean install -PunitTests
mvn clean install -PintegrationTests
mvn clean install -PuiTests
# all three of those:
mvn clean install -PallTests

Там нет модульного тестирования JavaScript с помощью Жасмин (или аналог). Вместо этого JavaScript, как и неявная логика в обогащенном Angular HTML, тестируется с помощью тестов Java + FluentSelenium. Для этого нет отчета о покрытии, и я вручную отслеживаю пути через кодовую базу JavaScript, чтобы можно было утверждать, что он проверен.

Этот стиль позволяет мне тестировать угловой компонент за один раз. Классическая лучшая практика Angular заключалась бы в том, чтобы сделать Jasmine для отдельных чистых функций JavaScript, а затем Karma (до настоящего времени) или, более вероятно, Protractor (в будущем). Почему (кроме предполагаемой тестовой разработки) мы должны разделить это на две фазы тестирования. Это означает, что мы с большей вероятностью попытаемся создать чисто JavaScript-функции, чтобы мы могли тестировать их в Jasmine. Это не так, как бизнес-цель. Конечно, Java + WebDriver медленнее, чем Protractor (я не представляю), но не намного медленнее. Если я могу «покрыть» свой полный код Тьюринга HTML и JavaScript за один раз, почему бы мне не сделать это?

Финальная составленная заявка (видео)

mvn clean jetty:run-war -Psites

Я делаю два «финальных» сайта. Тот, который использует управление аккордеоном из библиотеки Angular-UI Bootstrap (результаты поиска ниже критериев поиска), и тот, который использует набор вкладок (результаты поиска справа от критериев поиска). Поскольку это только подтверждение концепции, я назвал один index_1.html, а другой index_1.html, но они совершенно разные.

Вы должны развернуть эти два файла как index.html на двух разных доменах (и CDN в этом случае) и связать их с одним и тем же экземпляром сервлета. Или вы использовали бы Google AppEngine для размещения по одному, включая приложение сервлета (Greyhound.com в этом случае — ваш РЕАЛЬНЫЙ сервер). Мне нравится думать о AppEngine как о CDN со встроенным контейнером сервлета.

Два бренда:

Аккордеон (49 секунд, включая сборку)

Играйте с аккордеонами AngularUI здесь http://angular-ui.github.io/bootstrap (10% пути вниз по странице)

Tabset (24 сек)

Играйте с вкладками AngularUI здесь http://angular-ui.github.io/bootstrap (80% пути вниз по странице)

Full-Stack (UI) Тесты

Еще больше тестов с реальным контейнером сервлетов (без Moco) и, следовательно, с реальными сервисами Greyhound.com. Это вводит хрупкость, конечно. Плюс в том, что я повторно использовал код тестирования компонентов. Эти два бренда с помощью тестов WebDriver:

mvn clean install -Psites,fullStackTests

Подробнее о техническом решении:

Другой тип модульности

Как уже упоминалось ранее, SearchCriteria.html и SearchResults.html в основном независимы. Ну, кроме необходимости в серверных сервисах. Его JavaScript и HTML все в одном исходном файле. Это в значительной степени бесполезно в контексте производственного развертывания, но во время разработки / тестирования это «правильный размер» для интерактивных экспериментов и автоматизированного контроля качества (через WebDriver).

Для конечных сайтов Decdnorator проанализирует этот источник и удалит соответствующие фрагменты HTML и JavaScript. Предполагая, что все автоматизированные тесты пройдены, то есть. В этом случае Decdnorator также вставил компонент «Показать расписание» в компонент «Результаты поиска». Это могло бы быть иное, даже легкое наложение, даже с такой же легкостью. Рекомендованная Angular ‘Grunt’ toolchain имеет модуль htmlbuild, который имеет аналогичные возможности.

Вывод состоит в том, что страницы компонента HTML разработаны для тестируемости. Даже присоединение CSS не является необходимым на этом этапе.

Выбрасывать ребенка с водой?

Выражение для тех, кто не является родным на английском языке.

Многие подумают, что я зашел слишком далеко с этим. Вот что я мог бы сделать с более идиоматическим решением:

ngView и ngInclude

Это рекомендуемый командой разработчиков способ модуляции угловых фрагментов HTML. Они позволяют использовать такой HTML-код в нескольких местах приложения и разделять его для целей кодирования / тестирования (как правило, Jasmine и Karma). Я использую Decdnorator как плохое приближение этого. Мой недостаток в том, что HTML-фрагменты могут быть отправлены в браузер более одного раза, хотя многие утверждают, что это не будет проблемой, если что-то встроено и можно обналичить.

В своем приложении я сделал два «одностраничных приложения» (SPA) для двух разных брендов. Третий бренд может быть больше похож на компоненты, разделенные на страницы, которые были протестированы с WebDriver. Они были бы меньше, и было бы возможно сохранить все управление состояниями и обработку кнопок назад / вперед, которые мы хотим (да, у меня есть ошибки в этом отношении на момент публикации).

Angular’s $ маршруты и $ местоположение

Наряду с отображением http://example.com/resource/#/routeXyzв отдельные представления службы Angular’s $ router и $ location позволяют изменять URL-адреса без изменения страницы. По крайней мере, гораздо более гладко, чем сейчас. Это должно быть среди первых вещей, которые команда вернула бы против достойного сожаления использования, window.location.hrefкак сейчас. Если ничего другого, более сложному приложению рано или поздно понадобятся такие вещи.

Услуги, директивы и модули как таковые

В приложениях Classic Angular взаимодействие с сервером встроено в сервисы. Я просто использую контроллер для всего. Там нет никакой реальной разницы в аспектах времени выполнения либо, это только соображение разработчика. Чистота / увольнение было бы прерогативой для этого. Angular имеет некоторую возможность стиля внедрения зависимостей, чтобы убедиться, что функциональность JavaScript подключена соответствующим образом во время выполнения. В моем решении я тоже избегаю этого (несмотря на то, что являюсь одним из пионеров внедрения зависимостей), потому что мне это не нужно.

Области, $ apply и $ watch

Поскольку я прошиваю один контроллер, у меня действительно только одна область На самом деле использование формы HTML делает еще одну небольшую область (и я бы предпочел этого не делать). Я нигде не использую $ apply, и я не использую $ watch для любого объекта модели. Подробнее об этом в другой записи блога. Конечно, Angular-UI может использовать оба этих (и вложенных прицела) под капотом, но это не мое дело.

Жасмин, карма или транспортир

Это из команды Angular. Карму раньше называли Testacular , и команда до этого имела огромное признание критиков с JsTestDriver . Я не использую ничего из этого. Я ожидаю наибольшего количества критики за неявное предложение, которое я делаю из этого выбора.

Как Agile энтузиасты, мы долгое время стремились к разработке через тестирование (TDD). Исходя из этого, с помощью хороших методов насмешки мы утверждаем, что можем быстро двигаться, быстро рефакторизоваться, быстрее всего достичь «наиболее стабильного» для приложения и оставаться там, когда мы расширяем функциональность на протяжении ряда итераций. Затем есть «Тестовая пирамида» ( статья Мартина ), которая предполагает, что у нас должно быть много модульных тестов, меньше интеграционных тестов и еще меньше функциональных тестов. Без этого хорошего гибкого баланса мы идем к неприятностям, особенно если мы слишком сосредоточены на дизайне (или вообще не думаем о дизайне) ( статья Мартина «Гипотеза о выносливости дизайна» ).

Я не думаю, что здесь нет дизайна или слишком много дизайна. На самом деле все, что я изложил, это 1. Компонентные страницы, 2. Компонентные тесты с Java / Selenium и 3) сшивание битов для готового сайта. Я знал, что мне нужно изобретать, чтобы компоненты работали на одной шине.

Жасмин это тот, который заметно отсутствует. Java, FluentSelenium, ngWebDriver и WebDriver не являются заменой. Жасмин для чистых функций JavaScript, хотя. У меня только одна locnInError(..), и это четыре строки. Он получает покрытие через тесты компонентов WebDriver, даже если это покрытие второго класса. Конечно, многие из моих функций JavaScript можно разделить на две части — одну на чистом JavaScript и одну в зависимости от среды AngularJS в браузере. Это кажется мне безвозмездным.

Индикаторы ошибок

Angular обычно использует свойство $ error, скотч, для моделирования объектов. Мне это не очень нравится, поэтому я им не пользуюсь. Вместо этого у меня есть другой элемент с корнем в $ scope, называемый ошибками. Я должен вручную сбросить при каждой повторной проверке.

Еще больше об опыте разработки

Моко

Moco было забавно использовать, но хотелось бы, чтобы он был немного больше похож на Mockito. Вероятно, непреодолимым является тот факт, что сервер (обработчик HTTP Moco) и клиент (Java-вызов WebDriver через FluentSelenium) находятся в разных потоках, находясь под контролем TestNG. Это было бы то же самое для JUnit. Проблема заключается в том, что вы хотите потерпеть неудачу на стороне сервера, если возвращается параметр, который был неожиданным. Вы не можете сделать ошибку утверждения в потоке, который отвечает на входящие потоки HTTP, так как он не будет возвращаться в TestNG как сбой — он будет использован или, в лучшем случае, передан System.err. Таким образом, я делаю трюк StringBuilder для захвата параметров, а затем проверяю их обратно в основном потоке (это находится под контролем TestNG).

TestNG- и Guice-Inject вещи

TestNG (впервые для меня) интересен. JUnit всегда чувствовал себя статичным, и у меня есть проблема с этим, как правило, с учетом истории с Inversion of Control и Inpendency Injection, уходящей в прошлое на 14 и 10 лет соответственно. TestNG более ориентирован на конкретные экземпляры с точки зрения раннера и позволяет выполнять такие сложные вещи, как внедрение зависимостей. Я использую это для передачи в одном окне Firefox под управлением WebDriver / FluentSelenium. Все методы испытаний разделяют это — ууу! Проблема в том, что я обнаружил, что использую ModuleFactory (вещь Guice-TestNG), и бегун создал это N раз. Да, я только хотел, чтобы один модуль Guice делал один WebDriver для всех тестов. Итак, я сохраняю модуль как статическую переменную и делаю его только один раз с хреновым защитником:

благодаря

  • Кейт Моцидловски за помощь в экспериментах с Moco и заимствование услуг Greyhound.com.
  • Брэндону Байерсу за корректуру и комментарии — особенно о Жасмин и дизайне для тестирования.