Статьи

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

Конечный продукт
Что вы будете создавать

Protractor — это популярная комплексная тестовая среда, которая позволяет вам тестировать ваше приложение Angular в реальном браузере, имитируя взаимодействие браузера точно так же, как реальный пользователь взаимодействует с ним. Сквозные тесты предназначены для того, чтобы гарантировать, что приложение ведет себя так, как ожидается с точки зрения пользователя. Более того, тесты не касаются фактической реализации кода.

Protractor работает поверх популярного Selenium WebDriver, который представляет собой API для автоматизации и тестирования браузеров. В дополнение к функциям, предоставляемым Selenium WebDriver, Protractor предлагает локаторы и методы для захвата компонентов пользовательского интерфейса приложения Angular.

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

  • настройка, настройка и запуск транспортира
  • написание базовых тестов для транспортира
  • объекты страницы и почему вы должны их использовать
  • руководящие принципы, которые следует учитывать при написании тестов
  • написание тестов E2E для приложения от начала до конца

Разве это не звучит захватывающе? Однако обо всем по порядку.

Если вы использовали Angular-CLI, вы можете знать, что по умолчанию он поставляется с двумя платформами для тестирования. Они есть:

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

Очевидное различие между ними заключается в том, что первый используется для проверки логики компонентов и сервисов, а второй — для обеспечения того, чтобы функциональность высокого уровня (которая включает элементы пользовательского интерфейса) приложения работала так, как ожидалось.

Если вы новичок в тестировании в Angular, я бы порекомендовал прочитать серии «Тестирование компонентов в Angular, используя Jasmine», чтобы лучше понять, где провести черту.

В первом случае вы можете использовать возможности утилит тестирования Angular и Jasmine для написания не только модульных тестов для компонентов и сервисов, но и базовых тестов пользовательского интерфейса. Однако, если вам нужно протестировать интерфейсную функциональность вашего приложения от начала до конца, Protractor — это то, что вам нужно. API-интерфейс Protractor в сочетании с шаблонами проектирования, такими как объекты страниц, облегчают написание тестов, которые более читабельны. Вот пример, чтобы все пошло как по маслу.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
/*
  1. It should have a create Paste button
  2. Clicking the button should bring up a modal window
*/
 
it(‘should have a Create Paste button and modal window’, () => {
 
    expect(addPastePage.isCreateButtonPresent()).toBeTruthy(«The button should exist»);
    expect(addPastePage.isCreatePasteModalPresent()).toBeFalsy(«The modal window shouldn’t exist, not yet!»);
     
    addPastePage.clickCreateButton();
     
    expect(addPastePage.isCreatePasteModalPresent()).toBeTruthy(«The modal window should appear now»);
});

Настроить Protractor легко, если вы используете Angular-CLI для генерации вашего проекта. Структура каталогов, созданная с помощью ng new выглядит следующим образом.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
.
├── e2e
│  ├── app.e2e-spec.ts
│  ├── app.po.ts
│  └── tsconfig.e2e.json
├── karma.conf.js
├── package.json
├── package-lock.json
├── protractor.conf.js
├── README.md
├── src
│  ├── app
│  ├── assets
│  ├── environments
│  ├── favicon.ico
│  ├── index.html
│  ├── main.ts
│  ├── polyfills.ts
│  ├── styles.css
│  ├── test.ts
│  ├── tsconfig.app.json
│  ├── tsconfig.spec.json
│  └── typings.d.ts
├── tsconfig.json
└── tslint.json
 
5 directories, 19 files

Шаблон проекта по умолчанию, созданный Protractor, зависит от двух файлов для запуска тестов: файлов спецификаций, которые находятся в каталоге e2e, и файла конфигурации ( protractor.conf.js ). Давайте посмотрим, как настраивается protractor.conf.js :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/* Path: protractor.conf.ts*/
 
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
 
const { SpecReporter } = require(‘jasmine-spec-reporter’);
 
exports.config = {
  allScriptsTimeout: 11000,
  specs: [
    ‘./e2e/**/*.e2e-spec.ts’
  ],
  capabilities: {
    ‘browserName’: ‘chrome’
  },
  directConnect: true,
  baseUrl: ‘http://localhost:4200/’,
  framework: ‘jasmine’,
  jasmineNodeOpts: {
    showColors: true,
    defaultTimeoutInterval: 30000,
    print: function() {}
  },
  onPrepare() {
    require(‘ts-node’).register({
      project: ‘e2e/tsconfig.e2e.json’
    });
    jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
  }
};

Если вы согласны с выполнением теста в веб-браузере Chrome, вы можете оставить все как есть и пропустить оставшуюся часть этого раздела.

directConnect: true позволяет Protractor подключаться напрямую к драйверам браузера. Однако на момент написания этого руководства Chrome был единственным поддерживаемым браузером. Если вам нужна поддержка нескольких браузеров или вы запускаете браузер, отличный от Chrome, вам придется настроить автономный сервер Selenium. Шаги следующие.

Установите Protractor глобально, используя npm:

1
npm install -g protractor

Это устанавливает инструмент командной строки для webdriver-manager вместе с транспортиром. Теперь обновите менеджер webdriver, чтобы использовать последние двоичные файлы, а затем запустите автономный сервер Selenium.

1
2
3
webdriver-manager update
 
webdriver-manager start

Наконец, установите directConnect: false и добавьте свойство seleniumAddress следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
capabilities: {
   ‘browserName’: ‘firefox’
 },
 directConnect: false,
 baseUrl: ‘http://localhost:4200/’,
 seleniumAddress: ‘http://localhost:4444/wd/hub’,
 framework: ‘jasmine’,
 jasmineNodeOpts: {
   showColors: true,
   defaultTimeoutInterval: 30000,
   print: function() {}
 },

Файл конфигурации на GitHub предоставляет больше информации о параметрах конфигурации, доступных в Protractor. Я буду использовать параметры по умолчанию для этого урока.

ng e2e — единственная команда, которая необходима для запуска тестов, если вы используете Angular-CLI. Если тесты кажутся медленными, это потому, что Angular должен компилировать код каждый раз, когда вы запускаете ng e2e . Если вы хотите немного ускорить его, вот что вы должны сделать. Служите приложению, используя ng serve .

Затем запустите новую вкладку консоли и запустите:

1
ng e2e -s false

Теперь тесты должны загружаться быстрее.

Мы будем писать тесты E2E для базового приложения Pastebin. Клонируйте проект из репозитория GitHub .

Обе версии, начальная версия (без тестов) и финальная версия (с тестами), доступны в отдельных ветках. Клонируйте ветку стартера на данный момент. По желанию, обслуживайте проект и просмотрите код, чтобы ознакомиться с приложением под рукой.

Давайте кратко опишем наше приложение Pastebin. Первоначально приложение загрузит список таблиц (полученных с сервера-макета) в таблицу. В каждой строке таблицы будет кнопка View Paste , которая при нажатии открывает модальное окно начальной загрузки. В модальном окне отображаются данные вставки с вариантами редактирования и удаления вставки. В конце таблицы есть кнопка « Создать вставку» , которую можно использовать для добавления новых вставок.

Сквозное тестирование с использованием транспортира
Пример приложения.

Остальная часть урока посвящена написанию тестов на транспортир в Angular.

Файл спецификации, заканчивающийся на .e2e-spec.ts , будет содержать реальные тесты для нашего приложения. Мы разместим все тестовые спецификации в каталоге e2e, поскольку именно там мы настроили Protractor для поиска спецификаций.

При написании тестов Protractor необходимо учитывать две вещи:

  • Жасмин Синтаксис
  • Транспортир API

Для начала создайте новый файл с именем test.e2e-spec.ts со следующим кодом.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* Path: e2e/test.e2e-spec.ts */
 
import { browser, by, element } from ‘protractor’;
 
describe(‘Protractor Demo’, () => {
  
  beforeEach(() => {
    //The code here will get executed before each it block is called
    //browser.get(‘/’);
  });
 
  it(‘should display the name of the application’,() => {
   /*Expectations accept parameters that will be matched with the real value
   using Jasmine’s matcher functions.
   expect(«Pastebin Application»).toEqual(«Pastebin Application»);
    
  });
   
  it(‘should click the create Paste button’,() => {
    //spec goes here
    
  });
});

Это показывает, как наши тесты будут организованы внутри spec-файла с использованием синтаксиса Jasmine. beforeEach() describe() , beforeEach() и it() являются глобальными функциями Jasmine.

У Jasmine отличный синтаксис для написания тестов, и он так же хорошо работает с Protractor. Если вы новичок в Жасмин, я бы порекомендовал сначала пройти страницу GasHub Жасмин.

Блок описания используется для разделения тестов на наборы логических тестов. Каждый описывающий блок (или набор тестов) может иметь несколько блоков it (или тестовых спецификаций). Фактические тесты определены внутри тестовых спецификаций.

«Почему я должен структурировать свои тесты таким образом?» Вы можете спросить. Набор тестов можно использовать для логического описания конкретной функции вашего приложения. Например, все спецификации, относящиеся к компоненту Pastebin, в идеале должны быть включены в блок описания под названием Pastebin Page. Хотя это может привести к избыточным тестам, ваши тесты будут более читабельными и поддерживаемыми.

Блок описания может иметь метод beforeEach() который будет выполняться один раз перед каждой спецификацией в этом блоке. Таким образом, если вам нужен браузер для перехода к URL перед каждым тестом, размещение кода для навигации внутри beforeEach() — это то, что нужно.

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

Теперь давайте добавим немного плоти.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* Path: e2e/test.e2e-spec.ts */
 
import { browser, by, element } from ‘protractor’;
 
describe(‘Protractor Demo’, () => {
  
  beforeEach(() => {
    browser.get(‘/’);
  });
 
  it(‘should display the name of the application’,() => {
    
    expect(element(by.css(‘.pastebin’)).getText()).toContain(‘Pastebin Application’);
    
  });
   
  it(‘create Paste button should work’,() => {
    
    expect(element(by.id(‘source-modal’)).isPresent()).toBeFalsy(«The modal window shouldn’t appear right now «);
    element(by.buttonText(‘create Paste’)).click();
    expect(element(by.id(‘source-modal’)).isPresent()).toBeTruthy(‘The modal window should appear now’);
    
  });
});

browser.get('/') и element(by.css('.pastebin')).getText() являются частью API Protractor . Давайте испачкаем руки и перейдем прямо к тому, что может предложить транспортир.

Известные компоненты, экспортируемые Protractor API, перечислены ниже.

  1. browser() : Вы должны вызывать browser() для всех операций на уровне браузера, таких как навигация, отладка и т. д.
  2. element() : используется для поиска элемента в DOM на основе условия поиска или цепочки условий. Он возвращает объект ElementFinder, и вы можете выполнять над ними такие действия, как getText() или click() .
  3. element.all() : используется для поиска массива элементов, соответствующих некоторой цепочке условий. Возвращает объект ElementArrayFinder. Все действия, которые можно выполнить с ElementFinder, можно выполнить и с ElementArrayFinder.
  4. локаторы: Локаторы предоставляют методы для поиска элемента в приложении Angular.

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

  • by.css('selector-name') : На данный момент это наиболее часто используемый локатор для поиска элемента на основе имени селектора CSS.
  • by.name('name-value') : Находит элемент с соответствующим значением для атрибута name.
  • by.buttonText('button-value') : Находит элемент кнопки или массив элементов кнопки на основе внутреннего текста.

Примечание. Локаторы by.model, by.binding и by.repeater не работают с приложениями Angular 2+ на момент написания этого руководства. Вместо этого используйте CSS- локаторы.

Давайте напишем больше тестов для нашего приложения Pastebin.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
it(‘should accept and save input values’, () => {
      element(by.buttonText(‘create Paste’)).click();
 
      //send input values to the form using sendKeys
      
      element(by.name(‘title’)).sendKeys(‘Hello world in Ruby’);
      element(by.name(‘language’)).element(by.cssContainingText(‘option’, ‘Ruby’)).click();
      element(by.name(‘paste’)).sendKeys(«puts ‘Hello world’;»);
 
      element(by.buttonText(‘Save’)).click();
 
      //expect the table to contain the new paste
      const lastRow = element.all(by.tagName(‘tr’)).last();
      expect(lastRow.getText()).toContain(«Hello world in Ruby»);
});

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
it(‘should have an Create Paste button and modal window’, () => {
 
   expect(addPastePage.isCreateButtonPresent()).toBeTruthy(«The button should exist»);
   expect(addPastePage.isCreatePasteModalPresent()).toBeFalsy(«The modal window shouldn’t appear, not yet!»);
    
   addPastePage.clickCreateButton();
    
   expect(addPastePage.isCreatePasteModalPresent()).toBeTruthy(«The modal window should appear now»);
   
 
 });
  
it(‘should accept and save input values’, () => {
   
   addPastePage.clickCreateButton();
     
   //Input field should be empty initially
   const emptyInputValues = [«»,»»,»»];
   expect(addPastePage.getInputPasteValues()).toEqual(emptyInputValues);
    
   //Now update the input fields
   addPastePage.addNewPaste();
    
   addPastePage.clickSaveButton();
 
   expect(addPastePage.isCreatePasteModalPresent()).toBeFalsy(«The modal window should be gone»);
   expect(mainPage.getLastRowData()).toContain(«Hello World in Ruby»);
 
 });

Спецификации кажутся более простыми без дополнительного багажа транспортировщика. Как я это сделал? Позвольте мне представить вам страницу объектов.

Page Object — это шаблон дизайна, который популярен в кругах автоматизации тестирования. Объект страницы моделирует страницу или часть приложения, используя объектно-ориентированный класс. Все объекты (которые имеют отношение к нашим тестам), такие как текст, заголовки, таблицы, кнопки и ссылки, могут быть захвачены в объекте страницы. Затем мы можем импортировать эти объекты страницы в файл спецификации и вызывать их методы. Это уменьшает дублирование кода и облегчает обслуживание кода.

Создайте каталог с именем page-objects и добавьте в него новый файл с именем pastebin.po.ts . Все объекты, связанные с компонентом Pastebin, будут здесь захвачены. Как упоминалось ранее, мы разделили все приложение на три разных компонента, и каждому компоненту будет выделен объект страницы. Схема именования .po.ts является чисто условной, и вы можете назвать ее как угодно.

Вот план страницу, которую мы тестируем .

Сквозное тестирование в Angular Blueprint для компонента Pastebin

Вот код

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/* Path e2e/page-objects/pastebin.po.ts*/
 
import { browser, by, element, promise, ElementFinder, ElementArrayFinder } from ‘protractor’;
 
 
export class Pastebin extends Base {
     
    navigateToHome():promise.Promise<any> {
        return browser.get(‘/’);
    }
     
    getPastebin():ElementFinder {
        return element(by.css(‘.pastebin’));
    }
 
    /* Pastebin Heading */
    getPastebinHeading(): promise.Promise<string> {
        return this.getPastebin().element(by.css(«h2»)).getText();
    }
 
    /*Table Data */
 
    getTable():ElementFinder {
        return this.getTable().element(by.css(‘table’));
 
    }
 
    getTableHeader(): promise.Promise<string> {
        return this.getPastebin().all(by.tagName(‘tr’)).get(0).getText();
    }
 
    getTableRow(): ElementArrayFinder {
        return this.getPastebin().all(by.tagName(‘tr’));
    }
 
     
    getFirstRowData(): promise.Promise<string> {
        return this.getTableRow().get(1).getText();
    }
 
    getLastRowData(): promise.Promise<string> {
        return this.getTableRow().last().getText();
    }
 
    /*app-add-paste tag*/
 
    getAddPasteTag(): ElementFinder {
        return this.getPastebin().element(by.tagName(‘app-add-paste’));
    }
 
    isAddPasteTagPresent(): promise.Promise<boolean> {
        return this.getAddPasteTag().isPresent();
    }
 
}

Давайте рассмотрим то, что мы узнали до сих пор. API-интерфейс Protractor возвращает объекты, и мы до сих пор сталкивались с тремя типами объектов. Они есть:

  • promise.Promise
  • ElementFinder
  • ElementArrayFinder

Короче говоря, element() возвращает ElementFinder, а element().all All возвращает ElementArrayFinder. Вы можете использовать локаторы ( by.css , by.tagName и т. Д.), Чтобы найти местоположение элемента в DOM и передать его element() или element.all() .

Затем ElementFinder и ElementArrayFinder могут быть объединены в цепочку с такими действиями, как isPresent() , getText() , click() и т. Д. Эти методы возвращают обещание, которое разрешается после выполнения определенного действия.

Причина, по которой у нас нет цепочки then() в нашем тесте, заключается в том, что Protractor заботится об этом внутренне. Тесты кажутся синхронными, хотя они и не являются; следовательно, конечным результатом является линейное кодирование. Тем не менее, я рекомендую использовать синтаксис async / await, чтобы гарантировать, что код пригоден для будущего.

Вы можете ElementFinder несколько объектов ElementFinder , как показано ниже. Это особенно полезно, если в DOM есть несколько селекторов с одним и тем же именем, и нам нужно выбрать правильный.

1
2
3
4
getTable():ElementFinder {
       return this.getPastebin().element(by.css(‘table’));
 
   }

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/* Path: e2e/mainPage.e2e-spec.ts */
 
import { Pastebin } from ‘./page-objects/pastebin.po’;
import { browser, protractor } from ‘protractor’;
 
 
/* Scenarios to be Tested
  1. Pastebin Page should display a heading with text Pastebin Application
  2. It should have a table header
  3. The table should have rows
  4. app-add-paste tag should exist
*/
 
describe(‘Pastebin Page’, () => {
  
  const mainPage: Pastebin = new Pastebin();
 
  beforeEach(() => {
      mainPage.navigateToHome();
  });
 
  it(‘should display the heading Pastebin Application’, () => {
     
      expect(mainPage.getPastebinHeading()).toEqual(«Pastebin Application»);
 
      
  });
 
   it(‘should have a table header’, () => {
   
      expect(mainPage.getTableHeader()).toContain(«id Title Language Code»);
      
  })
  it(‘table should have at least one row’, () => {
     
      expect(mainPage.getFirstRowData()).toContain(«Hello world»);
  })
   
  it(‘should have the app-add-paste tag’, () => {
      expect(mainPage.isAddPasteTagPresent()).toBeTruthy();
  })
});

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

  • Отделите тесты E2E от юнит-тестов.
  • Группируйте свои тесты E2E разумно. Организуйте свои тесты так, чтобы они соответствовали структуре вашего проекта.
  • Если страниц несколько, объекты страниц должны иметь отдельный каталог.
  • Если у объектов страницы есть несколько общих методов (например, navigateToHome() ), создайте объект базовой страницы. Другие модели страниц могут наследовать от базовой модели страницы.
  • Сделайте свои тесты независимыми друг от друга. Вы не хотите, чтобы все ваши тесты не прошли из-за незначительного изменения в пользовательском интерфейсе, не так ли?
  • Держите определения объекта страницы без утверждений / ожиданий. Утверждения должны быть сделаны внутри спецификации файла.

Следуя приведенным выше рекомендациям, вот как должна выглядеть иерархия объекта страницы и организация файлов.

Иерархия объектов страницы и структура теста e2e в Protractor

Мы уже рассмотрели pastebin.po.ts и mainPage.e2e-spec.ts . Вот остальные файлы.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/* path: e2e/page-objects/base.po.ts */
 
import { browser, by, element, promise, ElementFinder, ElementArrayFinder } from ‘protractor’;
 
export class Base {
 
    /* Navigational methods */
    navigateToHome():promise.Promise<any> {
        return browser.get(‘/’);
    }
 
    navigateToAbout():promise.Promise<any> {
        return browser.get(‘/about’);
    }
 
    navigateToContact():promise.Promise<any> {
        return browser.get(‘/contact’);
    }
 
    /* Mock data for creating a new Paste and editing existing paste */
 
    getMockPaste(): any {
        let paste: any = { title: «Something here»,language: «Ruby»,paste: «Test»}
        return paste;
    }
 
    getEditedMockPaste(): any {
        let paste: any = { title: «Paste 2», language: «JavaScript», paste: «Test2» }
        return paste;
    }
     
    /* Methods shared by addPaste and viewPaste */
 
    getInputTitle():ElementFinder {
        return element(by.name(«title»));
    }
 
    getInputLanguage(): ElementFinder {
        return element(by.name(«language»));
    }
 
    getInputPaste(): ElementFinder {
        return element(by.name(«paste»));
 
    }
}
Сквозное тестирование в Angular Blueprint для компонента AddPaste
План для компонента AddPaste
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
/* Path: e2e/page-objects/add-paste.po.ts */
 
import { browser, by, element, promise, ElementFinder, ElementArrayFinder } from ‘protractor’;
import { Base } from ‘./base.po’;
export class AddPaste extends Base {
     
    getAddPaste():ElementFinder {
        return element(by.tagName(‘app-add-paste’));
    }
     
    /* Create Paste button */
    getCreateButton(): ElementFinder {
        return this.getAddPaste().element(by.buttonText(«create Paste»));
    }
 
    isCreateButtonPresent() : promise.Promise<boolean> {
        return this.getCreateButton().isPresent();
    }
 
    clickCreateButton(): promise.Promise<void> {
        return this.getCreateButton().click();
    }
 
    /*Create Paste Modal */
 
    getCreatePasteModal(): ElementFinder {
        return this.getAddPaste().element(by.id(«source-modal»));
    }
 
    isCreatePasteModalPresent() : promise.Promise<boolean> {
        return this.getCreatePasteModal().isPresent();
    }
 
    /*Save button */
    getSaveButton(): ElementFinder {
        return this.getAddPaste().element(by.buttonText(«Save»));
    }
     
    clickSaveButton():promise.Promise<void> {
        return this.getSaveButton().click();
    }
 
    /*Close button */
 
    getCloseButton(): ElementFinder {
        return this.getAddPaste().element(by.buttonText(«Close»));
    }
 
    clickCloseButton():promise.Promise<void> {
        return this.getCloseButton().click();
    }
     
 
    /* Get Input Paste values from the Modal window */
    getInputPasteValues(): Promise<string[]> {
        let inputTitle, inputLanguage, inputPaste;
 
        // Return the input values after the promise is resolved
        // Note that this.getInputTitle().getText doesn’t work
        // Use getAttribute(‘value’) instead
        return Promise.all([this.getInputTitle().getAttribute(«value»), this.getInputLanguage().getAttribute(«value»), this.getInputPaste().getAttribute(«value»)])
        .then( (values) => {
            return values;
        });
         
    }
 
    /* Add a new Paste */
 
    addNewPaste():any {
        let newPaste: any = this.getMockPaste();
 
        //Send input values
        this.getInputTitle().sendKeys(newPaste.title);
        this.getInputLanguage()
            .element(by.cssContainingText(‘option’, newPaste.language)).click();
        this.getInputPaste().sendKeys(newPaste.paste);
 
        //Convert the paste object into an array
        return Object.keys(newPaste).map(key => newPaste[key]);
 
    }
 
}
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/* Path: e2e/addNewPaste.e2e-spec.ts */
 
import { Pastebin } from ‘./page-objects/pastebin.po’;
import { AddPaste } from ‘./page-objects/add-paste.po’;
import { browser, protractor } from ‘protractor’;
 
/* Scenarios to be Tested
  1. AddPaste Page should have a button when clicked on should present a modal window
  2. The modal window should accept the new values and save them
  4. The saved data should appear in the MainPage
  3. Close button should work
*/
 
describe(‘Add-New-Paste page’, () => {
  
  const addPastePage: AddPaste = new AddPaste();
  const mainPage: Pastebin = new Pastebin();
 
  beforeEach(() => {
  
    addPastePage.navigateToHome();
  });
 
  it(‘should have an Create Paste button and modal window’, () => {
 
    expect(addPastePage.isCreateButtonPresent()).toBeTruthy(«The button should exist»);
    expect(addPastePage.isCreatePasteModalPresent()).toBeFalsy(«The modal window shouldn’t appear, not yet!»);
     
    addPastePage.clickCreateButton();
     
    expect(addPastePage.isCreatePasteModalPresent()).toBeTruthy(«The modal window should appear now»);
    
 
  });
 
  it(«should accept and save input values», () => {
    
    addPastePage.clickCreateButton();
      
    const emptyInputValues = [«»,»»,»»];
    expect(addPastePage.getInputPasteValues()).toEqual(emptyInputValues);
     
    const newInputValues = addPastePage.addNewPaste();
    expect(addPastePage.getInputPasteValues()).toEqual(newInputValues);
 
    addPastePage.clickSaveButton();
  
    expect(addPastePage.isCreatePasteModalPresent()).toBeFalsy(«The modal window should be gone»);
    expect(mainPage.getLastRowData()).toContain(«Something here»);
 
  });
 
  it(«close button should work», () => {
     
    addPastePage.clickCreateButton();
    addPastePage.clickCloseButton();
     
    expect(addPastePage.isCreatePasteModalPresent()).toBeFalsy(«The modal window should be gone»);
      
  });
   
});

Однако здесь не хватает нескольких вещей: тесты для кнопки « Вставить вид» и модальное окно, которое появляется после нажатия кнопки. Я собираюсь оставить это как упражнение для вас. Тем не менее, я дам вам подсказку.

Структура объектов страницы и спецификации для ViewPastePage аналогичны структуре AddPastePage.

Сквозное тестирование в Angular Blueprint для компонента ViewPaste
План для компонента ViewPaste

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

  1. Страница ViewPaste должна иметь кнопку, и при нажатии она должна вызвать модальное окно.
  2. Модальное окно должно отображать данные вставки недавно добавленной вставки.
  3. Модальное окно должно позволить вам обновить значения.
  4. Кнопка удаления должна работать.

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

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

Пожалуйста, дайте мне знать ваши мысли и опыт о написании тестов с использованием Protractor или о написании тестов для Angular в целом. Я хотел бы услышать их. Спасибо за прочтение!