Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Angular
Code

Тестирование компонентов в Angular с использованием Jasmine  часть 2, сервисы

by
Difficulty:IntermediateLength:LongLanguages:

Russian (Pусский) translation by Ilya Nikov (you can also view the original English article)

Final product image
What You'll Be Creating

Это вторая часть серии по тестированию в Angular с использованием Jasmine. В первой части учебника мы написали базовые модульные тесты для класса Pastebin и компонента Pastebin. Тесты, которые изначально были неудачными, позже стали зелеными.

Обзор

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

High level overview of things weve discussed in the previous tutorial and things we will be discussing in this tutorial

В этом уроке мы будем:

  • создавать новые компоненты и напишем дополнительные модульные тесты
  • напишем тесты для пользовательского интерфейса компонента
  • напишем модульные тесты для службы Pastebin
  • тестировать компоненты со входными данными
  • тестировать компонент с маршрутами

Давайте начнем!

Добавление пасты (продолжение)

Мы прошли половину процесса написания модульных тестов для компонента AddPaste. Здесь мы остановились в первой части серии.

Как уже упоминалось ранее, мы не будем писать строгие тесты пользовательского интерфейса. Вместо этого мы напишем некоторые базовые тесты для пользовательского интерфейса и рассмотрим способы тестирования логики компонента.

Действие click инициируется с помощью метода DebugElement.triggerEventHandler(), который является частью утилит тестирования Angular.

Компонент AddPaste по существу связан с созданием новых паст; поэтому в шаблоне компонента должна быть кнопка для создания новой пасты. При нажатии на кнопку должно появиться «модальное окно» с идентификатором «source-modal», которое в противном случае должно оставаться скрытым. Модальное окно будет создано с использованием Bootstrap; поэтому в шаблоне можно найти множество классов CSS.

Шаблон для компонента add-paste должен выглядеть примерно так:

Второй и третий тесты не дают никакой информации о деталях реализации компонента. Вот переработанная версия add-paste.component.spec.ts.

Пересмотренные тесты более ясны в том, что они прекрасно описывают логику компонента. Вот компонент AddPaste и его шаблон.

Тесты все равно должны потерпеть неудачу, потому что шпион в addPaste не находит такой метод в PastebinService. Вернемся к PastebinService и добавим в него немного кода.

Написание тестов для сервисов

Прежде чем приступить к написанию дополнительных тестов, добавим код в службу Pastebin.

addPaste() - метод службы для создания новых паст. http.post возвращает наблюдателя, который преобразуется в обещание с использованием метода toPromise(). Ответ преобразуется в формат JSON, и любые исключения во время выполнения обнаруживаются и сообщаются handleError().

Вы можете спросить: а разве мы не должны писать тесты для сервисов? И мой ответ - это определенно да. Сервисы, которые вводятся в компоненты Angular через инъекции зависимостей (DI), также подвержены ошибкам. Более того, тесты для сервисов относительно просты. Методы в PastebinService должны напоминать четыре операции CRUD с дополнительным методом обработки ошибок. Вот эти методы:

  • HandleError()
  • getPastebin()
  • addPaste()
  • updatePaste()
  • deletePaste()

Мы реализовали первые три метода в списке. Попробуем написать тесты для них. Вот блок описания.

Мы использовали TestBed.get(PastebinService), чтобы внедрить реальный сервис в наши тесты.

getPastebin возвращает массив объектов Pastebin. Проверка типа компиляции типа TypeScript не может использоваться для проверки того, что возвращаемое значение действительно представляет собой массив объектов Pastebin. Следовательно, мы использовали Object.getOwnPropertNames(), чтобы гарантировать, что оба объекта имеют одинаковые имена свойств.

Второй тест:

Оба теста должны пройти. Вот оставшиеся тесты.

Переработайте файл pastebin.service.ts с кодом для методов updatePaste() и deletePaste().

Назад к компонентам

Остальные требования для компонента AddPaste следующие:

  • Нажатие кнопки Save должно вызвать метод addPaste() службы Pastebin.
  • Если операция addPaste успешна, компонент должен выдать событие для уведомления родительского компонента.
  • Нажатие кнопки Close должно удалить идентификатор 'source-modal' из DOM и обновить свойство showModal в false.

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

Объявление всех переменных в корне описываемого блока является хорошей практикой по двум причинам. Переменные будут доступны внутри блока describe, в котором они были объявлены, и это делает тест более читаемым.

В приведенном выше тесте используется метод querySelector() для присвоения inputTitle, SelectLanguage и textAreaPaste соответствующих HTML-элементов (<input>, <select> и <textArea>). Затем значения этих элементов заменяются значениями свойств mockPaste. Это эквивалентно заполнению пользователем формы через браузер.

element.dispatchEvent(new Event("input")) запускает новое входное событие, чтобы шаблон знал, что значения поля ввода изменились. Тест предполагает, что входные значения должны распространяться в свойство newPaste компонента.

Объявите свойство newPaste следующим образом:

И обновите шаблон следующим кодом:

Дополнительные divs и классы для модального окна Bootstrap. [(ngModel)] - это директива Angular, которая реализует двустороннюю привязку данных. (click) = "onClose()" и (click) = "onSave()" являются примерами методов привязки событий, используемых для привязки события click к методу в компоненте. Вы можете узнать больше о различных методах привязки данных в официальном руководстве по Синтаксису Шаблонов Angular.

Если вы столкнулись с ошибкой Template Parse Error, это связано с тем, что вы не импортировали FormsModule в AppComponent.

Давайте добавим дополнительные спецификации в наш тест.

component.onSave() аналогичен вызову triggerEventHandler() в элементе кнопки Save. Поскольку мы добавили пользовательский интерфейс для кнопки, вызов component.save() выглядит более значимым. Оператор ожидания проверяет, были ли какие-либо вызовы шпионам. Вот окончательная версия компонента AddPaste.

Если операция onSave прошла успешно, компонент должен передать событие, сигнализирующее родительскому компоненту (компонент Pastebin), чтобы обновить его представление. Для этой цели служит addPasteSuccess, который является свойством события, украшенным декоратором @Output.

Тестировать компонент, который запускает выходное событие, довольно легко.

Тест подписывается на свойство addPasteSuccess так же, как это делает родительский компонент. Ожидание к концу подтверждает это. Наша работа над компонентом AddPaste завершена.

Раскомментируйте эту строку в файле pastebin.component.html:

И обновите pastebin.component.ts с помощью приведенного ниже кода.

Если вы столкнулись с ошибкой, это связано с тем, что вы не объявили компонент AddPaste в спецификационном файле компонента Pastebin. Было бы здорово, если бы мы могли описать все, что требовали наши тесты в одном месте, и импортировать это в наши тесты? Чтобы это произошло, мы могли либо импортировать AppModule в наши тесты, либо вместо этого создать новый модуль для наших тестов. Создайте новый файл и назовите его app-testing-module.ts:

Теперь вы можете заменить:

с:

Метаданные, определяющие providers и declarations, исчезли, и вместо этого импортируется AppTestingModule. Это здорово! TestBed.configureTestingModule() выглядит более чистым, чем раньше.

Просмотр, редактирование и удаление вставки

Компонент ViewPaste обрабатывает логику для просмотра, редактирования и удаления вставки. Конструкция этого компонента похожа на то, что мы сделали с компонентом AddPaste.

Mock design of the ViewPasteComponent in edit mode
Режим редактирования
Mock design of the ViewPasteComponent in view mode
Режим просмотра

Цели компонента ViewPaste перечислены ниже:

  • Шаблон компонента должен иметь кнопку View Paste.
  • Нажатие кнопки View Paste должно отображать модальное окно с идентификатором 'source-modal'.
  • Данные пасты должны распространяться от родительского компонента к дочернему компоненту и должны отображаться внутри модального окна.
  • Нажатие кнопки редактирования должно установить для параметра component.editEnabled значение true (editEnabled используется для переключения между режимом редактирования и режимом просмотра)
  • При нажатии кнопки Save следует вызвать метод updatePaste() службы Pastebin.
  • Щелчок по кнопке Delete должен вызвать метод deletePaste() службы Pastebin.
  • Успешные операции обновления и удаления должны выдать событие для уведомления родительского компонента о любых изменениях дочернего компонента.

Давайте начнем! Первые две спецификации идентичны тестам, которые мы ранее писали для компонента AddPaste.

Подобно тому, что мы делали ранее, мы создадим новый блок describe и разместим остальные спецификации внутри него. Вложение describe блоки таким образом делают файл спецификации более читаемым и существование функции описания более значимым.

Вложенный блок describe будет иметь функцию beforeEach(), где мы инициализируем два шпиона, один для метода updatePaste(), а другой для метода deletePaste(). Не забудьте создать объект mockPaste, так как наши тесты полагаются на него.

Вот тесты.

Тест предполагает, что компонент имеет свойство paste, которое принимает входные данные от родительского компонента. Ранее мы видели пример того, как события, испускаемые из дочернего компонента, могут быть протестированы без необходимости включать логику хост-компонента в наши тесты. Аналогично, для тестирования входных свойств проще сделать это, установив свойство в мок объект и ожидая, что значения мок объекта будут отображаться в HTML-коде.

В модальном окне будет много кнопок, и было бы неплохо написать спецификацию, чтобы гарантировать, что кнопки доступны в шаблоне.

Давайте исправим неудачные тесты, прежде чем переходить к более сложным тестам.

Простого просмотра пасты недостаточно. Компонент также отвечает за редактирование, обновление и удаление пасты. Компонент должен иметь свойство editEnabled, которое будет установлено в true, когда пользователь нажимает кнопку Edit paste.

Добавим editEnabled=true; к методу onEdit(), чтобы очистить первый оператор ожидания.

В приведенном ниже шаблоне директива ngIf используется для переключения между режимом просмотра и режимом редактирования. <ng-container> - это логический контейнер, который используется для группировки нескольких элементов или узлов.

Компонент должен иметь два источника событий Output(), один для свойства updatePasteSuccess, а другой для deletePasteSuccess. В приведенном ниже тесте проверяется следующее:

  1. Шаблон компонента принимает входные данные.
  2. Входные данные шаблона привязаны к свойству компонента paste.
  3. Если операция обновления прошла успешно, updatePasteSuccess генерирует событие с обновленной пастой.

Очевидная разница между этим тестом и предыдущими - использование функции fakeAsync. fakeAsync сопоставима с async, поскольку обе функции используются для запуска тестов в асинхронной тестовой зоне. Однако fakeAsync делает внешний вид теста более синхронным.

Метод tick() заменяет fixture.whenStable().then(), и код с точки зрения разработчика становится более читабельным. Не забудьте импортировать fakeAsync и tick из @angular/core/testing.

Наконец, вот спецификация для удаления пасты.

Мы почти закончили с компонентами. Вот окончательный черновик компонента ViewPaste.

Родительский компонент (pastebin.component.ts) необходимо обновить с помощью методов обработки событий, генерируемых дочерним компонентом.

Вот обновленный файл pastebin.component.html:

Настройка маршрутов

Чтобы создать маршрутизируемое приложение, нам понадобится еще несколько стоковых компонентов, чтобы мы могли создавать простые маршруты, ведущие к этим компонентам. Я создал компонент About и компонент Contact, чтобы мы могли поместить их в панель навигации. AppComponent будет содержать логику маршрутов. Мы напишем тесты для маршрутов после того, как закончим с ними.

Сначала импортируйте RouterModule и Routes в AppModuleAppTestingModule).

Затем определите маршруты и передайте определение маршрута методу RouterModule.forRoot.

Любые изменения, внесенные в AppModule, также должны быть внесены в AppTestingModule. Но если вы столкнулись с ошибкой No base href set при выполнении тестов, добавьте следующую строку в ваш массив providers в AppTestingModule.

Теперь добавьте следующий код в app.component.html.

routerLink - это директива, которая используется для привязки элемента HTML к маршруту. Мы использовали его с тегом привязки HTML. RouterOutlet - это еще одна директива, которая отмечает место в шаблоне, где должно отображаться представление маршрутизатора.

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

Если все будет хорошо, вы должны увидеть что-то вроде этого.

Screenshot of Karma test runner on Chrome displaying the final test results

Заключительные штрихи

Добавьте красивый Bootstrap дизайн в свой проект и запустите свой проект, если вы еще этого не сделали.

Резюме

Мы написали полное приложение с нуля в тестовой среде. Разве это не круто? В этом уроке мы узнали:

  • как разработать компонент, используя подход с написанием тестов
  • как писать модульные тесты и базовые тесты пользовательского интерфейса для компонентов
  • об утилитах тестирования Angular и о том, как включить их в наши тесты
  • об использовании async() и fakeAsync() для запуска асинхронных тестов
  • основы маршрутизации в тестах Angular и написании тестов для маршрутов

Надеюсь, вам понравился рабочий процесс TDD. Пожалуйста, свяжитесь с нами через комментарии и сообщите нам ваше мнение!

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.