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

Тестирование Angular компонентов с использованием Jasmine: часть 1

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

Test Driven Development - это практика программирования, которую проповедовали и продвигали все сообщества разработчиков на планете. И все же это рутина, которая в значительной степени игнорируется разработчиком при изучении нового фреймворка. Написание модульных тестов с самого начала проекта поможет вам лучше писать код, выявлять ошибки и поддерживать более эффективный рабочий процесс разработки.

Разработка с использованием тестов в Angular

Angular является полно-функциональной платформой для фронт-енд разработки и имеет собственный набор инструментов для тестирования. В этом учебнике мы будем использовать следующие инструменты:

  • Jasmine Framework. Jasmine - популярный фреймворк для тестирования JavaScript. С Jasmine вы можете написать тесты, которые будут более выразительны и понятны. Вот пример для начала.
  • Karma Test Runner. Karma - это инструмент, который позволяет тестировать ваше приложение в нескольких браузерах. У Karma есть плагины для браузеров, таких как Chrome, Firefox, Safari и многих других. Но я для тестирования предпочитаю использовать headless браузер. В headless браузере отсутствует графический интерфейс, и таким образом вы можете сохранить результаты теста внутри своего терминала. В этом уроке мы настроим Karma для работы с Chrome и, а также дополнительно с headless версией Chrome.
  • Angular Testing Utilities. Утилиты тестирования Angular предоставляют вам библиотеку для создания тестовой среды для вашего приложения. Классы, такие как TestBed и ComponentFixtures, и вспомогательные функции, такие как async и fakeAsync, являются частью пакета @angular/core/testing. Знакомство с этими утилитами необходимо, если вы хотите написать тесты, которые показывают, как ваши компоненты взаимодействуют со своими собственными шаблонами, сервисами и другими компонентами.

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

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

Наша цель

Цель этого учебника - создать интерфейс для приложения Pastebin в тестовой среде разработки. В этом уроке мы будем следовать популярной мантре TDD: «красный/зеленый/рефакторинг». Мы будем писать тесты, которые первоначально не выполняются (красные), а затем мы работаем над нашим кодом, чтобы они проходили (зеленые). Мы так же будем рефакторить наш код, когда он начнет вонять, что означает, что он раздувается и становится уродлив.

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

Structure of the application we are about to build Components services and modules are highlighted

В первой части серии мы сосредоточимся только на создании тестовой среды и написании базовых тестов для компонентов. Angular является фреймворком, основанным на компонентах; поэтому необходимо потратить некоторое время, чтобы познакомиться с написанием тестов для компонентов. Во второй части серии мы напишем более сложные тесты для компонентов, компонентов с входными данными, маршрутизируемыми компонентами и службами. К концу серии у нас будет полностью функционирующее приложение Pastebin, которое будет выглядеть так.

Screenshot of the front-end of the application
Вид компонента Pastebin
Screenshot of the front-end of the application
Вид компонента AddPaste
ViewPaste component in action using a bootstrap modal
Вид компонента ViewPaste

В этом уроке вы узнаете, как:

  • настроить Jasmine и Karma
  • создать класс Pastebin
  • создать каркас для PastebinService
  • создать два компонента, Pastebin и AddPaste
  • написать единичные тесты

Весь код для учебника доступен на Github.

Клонируйте репозиторий и не стесняйтесь проверить код, если у вас возникнут сомнения на любом этапе этого урока. Давайте начнем!

Настройка Jasmine и Karma

Разработчики в Angular упростили создание нашей тестовой среды. Для начала нам нужно сначала установить Angular. Я предпочитаю использовать Angular-CLI. Это решение «все-в-одном», которое заботится о создании, генерации, сборке и тестировании вашего Angular проекта.

Вот структура каталогов, созданная Angular-CLI.

Поскольку нас больше интересует тестирование в Angular, нам нужно найти два типа файлов.

karma.conf.js - это файл конфигурации для тестировщика Karma и единственный файл конфигурации, который нам понадобится для написания модульных тестов в Angular. По умолчанию в качестве браузера для Karma используется Chrome. Мы создадим свой собственный ланчер для запуска headless Chrome и добавим его в массив browsers.

Другим типом файла, который нужно найти, является все, что заканчивается на .spec.ts. По соглашению тесты, написанные на Jasime, называются спецификациями. Все тестовые спецификации должны быть расположены внутри каталога приложения src/app/, поскольку именно здесь Karma ищет тестовые спецификации. Если вы создаете новый компонент или службу, важно, чтобы вы размещали ваши тестовые спецификации в том же каталоге, в котором находится код компонента или службы.

Команда ng new создала файл app.component.spec.ts для нашего app.component.ts. Откройте его и хорошенько рассмотрите тесты Jasmine в Angular. Даже если этот код для вас не имеет никакого смысла, это нормально. Мы сохраним AppComponent так, как он выглядит сейчас, и используем его для размещения маршрутов далее в учебнике.

Создание класса Pastebin

Нам нужен класс Pastebin для моделирования нашего Pastebin внутри компонентов и тестов. Вы можете создать его с помощью Angular-CLI.

Добавьте в Pastebin.ts следующую логику:

Мы определили класс Pastebin, и каждый экземпляр этого класса будет иметь следующие свойства:

  • id
  • title
  • language
  • paste

Создайте еще один файл с именем pastebin.spec.ts для набора тестов.

Набор тестов начинается с блока describe, который является глобальной функцией Jasmine, которая принимает два параметра. Первый параметр - это название набора тестов, а второй - его фактическая реализация. Спецификации определяются с использованием функции it, которая принимает два параметра, аналогичные параметрам блока describe.

Множество спецификаций (блоков it) можно вставить внутри набора тестов (блок describe). Тем не менее, убедитесь, что имена тестовых наборов названы так, что они недвусмысленны и читаемы, потому что они предназначены для использования в качестве документации для читателя.

Ожидания, реализованные с использованием функции expect, используются Jasmine для определения того, должна ли спецификация проходить или терпеть неудачу. Функция expect принимает параметр, который известен как фактическое значение. Затем он соединен цепью с другой функцией, которая принимает ожидаемое значение. Эти функции называются вспомогательными функциями, и в этом учебнике мы будем использовать функции матчинга, такие как toBeTruthy(), toBeDefined(), toBe() и toContain().

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

Мы создали экземпляр класса Pastebin и добавили несколько ожиданий в нашу тестовую спецификацию. Запустите ng test, чтобы проверить, что все тесты зеленые.

Создание каркаса для службы

Создайте службу, используя следующую команду.

В PastebinService будет установлена логика отправки HTTP-запросов на сервер; однако у нас нет API-интерфейса сервера для создаваемого нами приложения. Поэтому мы собираемся моделировать связь с сервером с помощью модуля, известного как InMemoryWebApiModule.

Настройка Angular-in-Memory-Web-API

Установите angular-in-memory-web-api через npm:

Обновите AppModule до этой версии.

Создайте InMemoryDataService, который реализует InMemoryDbService.

Здесь pastebin представляет собой массив выборочных паст, которые будут возвращены или обновлены при выполнении действия HTTP, например http.get или http.post.

Метод getPastebin() выполняет запрос HTTP.get и возвращает обещание, которое разрешается в массив объектов Pastebin, возвращаемых сервером.

Если при запуске спецификации вы получаете сообщение No provider for HTTP error, то  вам необходимо импортировать HTTPModule в соответствующий файл spec.

Начало работы с компонентами

Компоненты - это самый базовый строительный блок пользовательского интерфейса в приложении Agnular. Angular приложение представляет собой дерево компонентов.
- Angular документация

Как было отмечено ранее в разделе «Обзор», мы будем работать над двумя компонентами в этом уроке: PastebinComponent и AddPasteComponent. Компонент Pastebin состоит из таблицы, в которой перечислены все вставки, полученные с сервера. Компонент AddPaste содержит логику создания новых паст.

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

Идем дальше и создаем компоненты с помощью Angular-CLI.

Параметр --spec = false указывает Angular CLI не создавать спецификационный файл. Это связано с тем, что мы хотим писать модульные тесты для компонентов с нуля. Создайте файл pastebin.component.spec.ts внутри папки pastebin-component.

Вот код для pastebin.component.spec.ts.

Здесь многое происходит. Давайте разложим его и рассмотрим по блокам. Внутри блока describe мы объявили некоторые переменные, а затем мы использовали функцию beforeEach. beforeEach() является глобальной функцией, предоставляемой Jasmine, и, как следует из названия, она вызывается один раз перед каждой спецификацией в блоке describe, в котором она вызывается.

Класс TestBed является частью утилит тестирования Angular, и он создает модуль тестирования, аналогичный модулю @NgModule. Кроме того, вы можете настроить TestBed с помощью метода configureTestingModule. Например, вы можете создать тестовую среду для своего проекта, которая эмулирует фактическое приложение Angular, и затем вы можете вытащить компонент из вашего прикладного модуля и снова присоединить его к этому тестовому модулю.

Из документации Angular:

Метод createComponent возвращает компонент ComponentFixture, дескриптор тестовой среды, окружающей созданный компонент. Фикстура обеспечивает доступ к самому экземпляру компонента и к DebugElement, который является дескриптором элемента DOM компонента.

Как уже упоминалось выше, мы создали фикстуру PastebinComponent, а затем использовали эту фикстуру для создания экземпляра компонента. Теперь мы можем получить доступ к свойствам и методам компонента внутри наших тестов, вызвав имя comp.property_name. Поскольку фикстура также обеспечивает доступ к debugElement, мы теперь можем запросить элементы DOM и селектора.

В нашем коде есть проблема, о которой мы еще не подумали. Наш компонент имеет внешний шаблон и файл CSS. Извлечение и чтение их из файловой системы - это асинхронная активность, в отличие от остальной части кода, которая является синхронной.

Angular предлагает вам функцию async(), которая заботится обо всех асинхронных вещах. async занимается тем, что отслеживает все асинхронные задачи внутри него, скрывая от нас сложность асинхронного выполнения. Итак, теперь у нас будет две функции beforeEach, асинхронная beforeEach() и синхронная beforeEach().

Мы еще не написали никаких тестовых спецификаций. Тем не менее, это хорошая идея, чтобы заранее составить схему спецификаций. На рисунке ниже изображен грубый дизайн компонента Pastebin.

Mock design of the Pastebin Application

Нам нужно написать тесты со следующими ожиданиями.

  • Компонент Pastebin должен существовать.
  • Свойство title компонента должно отображаться в шаблоне.
  • Шаблон должен иметь таблицу HTML для отображения существующих паст.
  • pastebinService вводится в компонент, и его методы доступны.
  • Пасты не должны отображаться до тех пор, пока не вызывается onInit().
  • Пасты не отображаются до тех пор, пока обещание в нашем Service не будет разрешено.

Первые три теста легко реализовать.

В тестовой среде Angular автоматически не связывает свойства компонента с элементами шаблона. Вы должны явно вызвать fixture.detectChanges() каждый раз, когда вы хотите связать свойство компонента с шаблоном. Запуск теста должен дать вам ошибку, потому что мы еще не объявили свойство title внутри нашего компонента.

Не забудьте обновить шаблон базовой структурой таблицы.

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

Вместо этого мы должны написать тесты, в которых основное внимание будет сосредоточено на том, взаимодействует ли компонент с сервисом, как ожидалось. Мы добавим спецификации, которые будут следить за pastebinService и его методом getPastebin().

Во-первых, импортируйте PastebinService в наш тестовый набор.

Затем добавьте его в массив providers внутри TestBed.configureTestingModule().

В приведенном ниже коде создается шпион Jasmine, который предназначен для отслеживания всех вызовов метода getPastebin() и возвращает обещание, которое немедленно разрешается в mockPaste.

Шпион не заботится о деталях реализации реального сервиса, но вместо этого перехватывает любой вызов фактического метода getPastebin(). Более того, все удаленные вызовы, скрытые внутри getPastebin(), игнорируются нашими тестами. Во второй части учебника мы будем писать изолированные модульные тесты для сервисов Angular.

Добавьте следующие тесты в pastebin.component.spec.ts.

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

Во втором spec компонент инициализируется (поскольку вызывается fixture.detectChanges()), и ожидается, что шпион будет вызван, но шаблон не должен обновляться. Несмотря на то, что шпион возвращает разрешенное обещание, mockPaste пока недоступен. Он не должен быть доступен, если тест не является асинхронным.

В третьем тесте используется функция async(), обсуждавшаяся ранее, для запуска теста в тестовой зоне асинхронного тестирования. async() используется для синхронного тестирования асинхронного. fixture.whenStable() вызывается, когда все ожидающие асинхронные действия выполнены, а затем вызывается второй раунд fixture.detectChanges() для обновления DOM с новыми значениями. Ожидание в финальном тесте гарантирует, что наш DOM обновляется с помощью значений mockPaste.

Чтобы пройти тесты, нам необходимо обновить наш pastebin.component.ts следующим кодом.

Также необходимо обновить шаблон.

Добавление новой пасты

Создайте компонент AddPaste с помощью Angular-CLI. На рисунке ниже изображен дизайн компонента AddPaste.

Mock design of the AddPaste component

Логика компонента должна соответствовать следующим спецификациям.

  • Шаблон компонента AddPaste должен иметь кнопку Create Paste.
  • Нажатие кнопки Create Paste должно отображать модальный блок с идентификатором 'source-modal'.
  • Действие click также должно обновить свойство showModal компонента в true. (showModal - это логическое свойство, которое становится истинным, когда модальное окно отображается и false, когда оно закрывается.)
  • Нажатие кнопки save вызовет метод addPaste() службы Pastebin.
  • При нажатии кнопки close следует удалить идентификатор 'source-modal' из DOM и обновить свойство showModal в false.

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

DebugElement.triggerEventHandler() - единственное новое для вас. Он используется для запуска события клика на элемент кнопки, на который он вызывается. Второй параметр - это объект события, и мы оставили его пустым, так как click() компонента не ожидает его.

Резюме

Вот и все. В этой первой статье мы узнали:

  • как настроить и настроить Jasmine и Karma
  • как написать базовые тесты для классов
  • как проектировать и писать модульные тесты для компонентов
  • как создать базовый сервис
  • как использовать утилиты тестирования Angular в нашем проекте

В следующем уроке мы создадим новые компоненты, напишем больше тестовых компонентов с входными данными, сервисами и маршрутами. Оставайтесь с нами во второй части серии. Поделитесь своими мыслями в комментариях.

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.