83
Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты на JS для тестирования Angular: плюсы, минусы, подводные камни

Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

  • Upload
    others

  • View
    43

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

Гейзенбаг, 2017-06-04 Илья Коробицын

Пишем Selenium тесты на JS для тестирования Angular: плюсы,

минусы, подводные камни

Page 2: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

2

О докладчике Илья Коробицын

mailto: [email protected] Telegram: @korobochka

Работаю в Grid Dynamics

Занимаемся автоматизациейтестирования

Бэкенд на Java Фронтенд на JavaScript

Page 3: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

3

План доклада Какие сложности приходится решать при написании Selenium

тестов для современных веб приложений

Что такое Protractor и как он помогает с тестированием Angular приложений

С какими неожиданностями при этом придётся столкнуться

Чему можно научиться из всего этого

Page 4: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

4

Подопытное приложение

Page 5: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

5

Подопытное приложение

Page 6: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

6

Подопытное приложение

Page 7: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

7

Немного про Angular

Page 8: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

8

Немного про Angular

<tr ng-repeat="result in results"> <td> {{result.timestamp | date:'mediumTime'}} </td> <td>{{result.nick}}</td> <td>{{result.age}}</td> <td>{{result.result}}</td></tr>

Page 9: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

9

Немного про Angular

.......

<tr ng-repeat="result in results" class="ng-scope"> <td class="ng-binding"> 5:36:21 PM </td> <td class="ng-binding">CoolNick</td> <td class="ng-binding">15</td> <td class="ng-binding">CoolNick2002</td></tr><tr ng-repeat="result in results" class="ng-scope"> <td class="ng-binding"> 5:36:04 PM </td> <td class="ng-binding">Nick</td> <td class="ng-binding">25</td> <td class="ng-binding">Nick1992</td></tr>.......

.......

<tr ng-repeat="result in results" class="ng-scope"> <td class="ng-binding"> 5:36:21 PM </td> <td class="ng-binding">CoolNick</td> <td class="ng-binding">15</td> <td class="ng-binding">CoolNick2002</td></tr><tr ng-repeat="result in results" class="ng-scope"> <td class="ng-binding"> 5:36:04 PM </td> <td class="ng-binding">Nick</td> <td class="ng-binding">25</td> <td class="ng-binding">Nick1992</td></tr>.......

Page 10: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

10

Немного про Angular

.......

<tr ng-repeat="result in results" class="ng-scope"> <td class="ng-binding"> 5:36:21 PM </td> <td class="ng-binding">CoolNick</td> <td class="ng-binding">15</td> <td class="ng-binding">CoolNick2002</td></tr><tr ng-repeat="result in results" class="ng-scope"> <td class="ng-binding"> 5:36:04 PM </td> <td class="ng-binding">Nick</td> <td class="ng-binding">25</td> <td class="ng-binding">Nick1992</td></tr>.......

.......

<tr ng-repeat="result in results" class="ng-scope"> <td class="ng-binding"> 5:36:21 PM </td> <td class="ng-binding">CoolNick</td> <td class="ng-binding">15</td> <td class="ng-binding">CoolNick2002</td></tr><tr ng-repeat="result in results" class="ng-scope"> <td class="ng-binding"> 5:36:04 PM </td> <td class="ng-binding">Nick</td> <td class="ng-binding">25</td> <td class="ng-binding">Nick1992</td></tr>.......

Page 11: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

11

Немного про Angular

.......

<tr ng-repeat="result in results" class="ng-scope"> <td class="ng-binding"> 5:36:21 PM </td> <td class="ng-binding">CoolNick</td> <td class="ng-binding">15</td> <td class="ng-binding">CoolNick2002</td></tr><tr ng-repeat="result in results" class="ng-scope"> <td class="ng-binding"> 5:36:04 PM </td> <td class="ng-binding">Nick</td> <td class="ng-binding">25</td> <td class="ng-binding">Nick1992</td></tr>.......

.......

<tr ng-repeat="result in results" class="ng-scope"> <td class="ng-binding"> 5:36:21 PM </td> <td class="ng-binding">CoolNick</td> <td class="ng-binding">15</td> <td class="ng-binding">CoolNick2002</td></tr><tr ng-repeat="result in results" class="ng-scope"> <td class="ng-binding"> 5:36:04 PM </td> <td class="ng-binding">Nick</td> <td class="ng-binding">25</td> <td class="ng-binding">Nick1992</td></tr>.......

Page 12: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

12

Про сложности Элементами на странице управляет Angular, а не программисты

Те не ставят id, так как они им не нужны

Все элементы получаются безликие, связь с данными неявна

Тяжело писать селекторы

Page 13: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

13

Подопытное приложение

Page 14: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

14

Про сложности Обновлением элементов на странице также занимается

фреймворк

Происходит это после определённых триггеров Завершение HTTP запроса Таймер

На каждое обновление приходится писать отдельный waiter

Page 15: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

15

Посмотрим на тесты

Page 16: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

16

Пример на Java

@Testpublic void testResultAddedToHistory() throws Exception {

final List<WebElement> inputs = driver.findElements(By.tagName("input"));

inputs.get(0).sendKeys("Korobochka");inputs.get(1).sendKeys("17");driver.findElement(By.className("btn")).click();new WebDriverWait(driver, 10).until(webDriver ->

!webDriver.findElement(By.tagName("h3")).getText() .contains("Loading"));

WebElement targetElement = driver.findElement( By.xpath("//tbody/tr[1]/td[last()]"));

assertEquals(targetElement.getText(), "Korobochka2000");}

Page 17: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

17

Пример на Java

@Testpublic void testResultAddedToHistory() throws Exception {

final List<WebElement> inputs = driver.findElements(By.tagName("input"));

inputs.get(0).sendKeys("Korobochka");inputs.get(1).sendKeys("17");driver.findElement(By.className("btn")).click();new WebDriverWait(driver, 10).until(webDriver ->

!webDriver.findElement(By.tagName("h3")).getText() .contains("Loading"));

WebElement targetElement = driver.findElement( By.xpath("//tbody/tr[1]/td[last()]"));

assertEquals(targetElement.getText(), "Korobochka2000");}

Page 18: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

18

Пример на Java

@Testpublic void testResultAddedToHistory() throws Exception {

final List<WebElement> inputs = driver.findElements(By.tagName("input"));

inputs.get(0).sendKeys("Korobochka");inputs.get(1).sendKeys("17");driver.findElement(By.className("btn")).click();new WebDriverWait(driver, 10).until(webDriver ->

!webDriver.findElement(By.tagName("h3")).getText() .contains("Loading"));

WebElement targetElement = driver.findElement( By.xpath("//tbody/tr[1]/td[last()]"));

assertEquals(targetElement.getText(), "Korobochka2000");}

Page 19: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

19

Protractor

Page 20: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

20

Пример с Protractor

it('should add latest result to history', function() {

element(by.model('nick')).sendKeys('Korobochka'); element(by.model('age')).sendKeys('17'); element(by.buttonText('Submit')).click();

let fistRow = element.all(by.repeater('result in memory')).get(0); let firstRowResult = fistRow.element(by.binding('result.result')); expect(firstRowResult.getText()).toEqual('Korobochka2000');});

Page 21: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

21

Пример с Protractor

it('should add latest result to history', function() {

element(by.model('nick')).sendKeys('Korobochka'); element(by.model('age')).sendKeys('17'); element(by.buttonText('Submit')).click();

let fistRow = element.all(by.repeater('result in memory')).get(0); let firstRowResult = fistRow.element(by.binding('result.result')); expect(firstRowResult.getText()).toEqual('Korobochka2000');});

Page 22: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

22

Пример с Protractor

it('should add latest result to history', function() {

element(by.model('nick')).sendKeys('Korobochka'); element(by.model('age')).sendKeys('17'); element(by.buttonText('Submit')).click();

let fistRow = element.all(by.repeater('result in memory')).get(0); let firstRowResult = fistRow.element(by.binding('result.result')); expect(firstRowResult.getText()).toEqual('Korobochka2000');});

Page 23: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

23

Angular 2+

StartedF

Failures:1) Protractor Demo App should generate nickname correctly Message: Failed: unknown error: angular is not defined (Session info: chrome=58.0.3029.81) (Driver info: chromedriver=2.27.440175 ….)

Page 24: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

24

Angular 2+

Page 25: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

25

Angular 2+

/** @deprecated use findProviders */findBindings(using: any, provider: string, exactMatch: boolean): any[] { // TODO(juliemr): implement. return [];}

findProviders(using: any, provider: string, exactMatch: boolean): any[] { // TODO(juliemr): implement. return [];}

Page 26: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

26

Итого Если у вас Angular 1, то Protractor поможет с селекторами

Если Angular 2+, то увы Пинайте разработчиков добавлять id или уникальные классы в

разметку

Вам почти никогда не придётся писать ожидания

Page 27: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

27

Паззлеры

Page 28: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

28

Паззлеры Будем тестировать наше подопытное приложение с помощью

Protractor

Я покажу тест и несколько варинтов развития событий (пройдёт / не пройдёт / etc )

Зал голосует

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

Кто-то кратко рассказывает, почему так произошло

Page 29: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

29

Паззлер №1

Page 30: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

30

Паззлер №1

it('should display consistent info in list', function() { doInputAndClick('Nick', '17');

let listItem = element.all(by.className('results-item')).get(0); let nick = listItem.all(by.tagName('td')).get(1).getText(); let age = listItem.all(by.tagName('td')).get(2).getText();

let actualNickname = listItem.element(by.className('results-result')).getText();

expect(actualNickname).toEqual(nick + (2017 - age));});

Page 31: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

31

Паззлер №1

it('should display consistent info in list', function() { doInputAndClick('Nick', '17');

let listItem = element.all(by.className('results-item')).get(0); let nick = listItem.all(by.tagName('td')).get(1).getText(); let age = listItem.all(by.tagName('td')).get(2).getText();

let actualNickname = listItem.element(by.className('results-result')).getText();

expect(actualNickname).toEqual(nick + (2017 - age));});

Вариант 1: Тест успешно проходит

Page 32: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

32

Паззлер №1

it('should display consistent info in list', function() { doInputAndClick('Nick', '17');

let listItem = element.all(by.className('results-item')).get(0); let nick = listItem.all(by.tagName('td')).get(1).getText(); let age = listItem.all(by.tagName('td')).get(2).getText();

let actualNickname = listItem.element(by.className('results-result')).getText();

expect(actualNickname).toEqual(nick + (2017 - age));});

Вариант 2: Undefined is not a function

Page 33: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

33

Паззлер №1

it('should display consistent info in list', function() { doInputAndClick('Nick', '17');

let listItem = element.all(by.className('results-item')).get(0); let nick = listItem.all(by.tagName('td')).get(1).getText(); let age = listItem.all(by.tagName('td')).get(2).getText();

let actualNickname = listItem.element(by.className('results-result')).getText();

expect(actualNickname).toEqual(nick + (2017 - age));});

Вариант 3: Fail: Expected 'Nick2000' to equal 'NickNaN'

Page 34: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

34

Паззлер №1

it('should display consistent info in list', function() { doInputAndClick('Nick', '17');

let listItem = element.all(by.className('results-item')).get(0); let nick = listItem.all(by.tagName('td')).get(1).getText(); let age = listItem.all(by.tagName('td')).get(2).getText();

let actualNickname = listItem.element(by.className('results-result')).getText();

expect(actualNickname).toEqual(nick + (2017 - age));});

Вариант 4: Fail: Expected 'Nick2000' to equal '[object Object]NaN'

Page 35: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

35

Паззлер №1it('should display consistent info in list', function() { doInputAndClick('Nick', '17');

let listItem = element.all(by.className('results-item')).get(0); let nick = listItem.all(by.tagName('td')).get(1).getText(); let age = listItem.all(by.tagName('td')).get(2).getText();

let actualNickname = listItem.element(by.className('results-result')).getText();

expect(actualNickname).toEqual(nick + (2017 - age));});

Вариант 1: Тест успешно проходитВариант 2: Undefined is not a functionВариант 3: Fail: Expected 'Nick2000' to equal 'NickNaN'Вариант 4: Fail: Expected 'Nick2000' to equal '[object Object]NaN'

Page 36: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

36

Паззлер №1: Запускаем....

Failures:1) Protractor Demo App should display consistent info in list Message: Expected 'Nick2000' to equal '[object Object]NaN'. Stack: Error: Failed expectation

Page 37: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

37

Асинхронность Одна из основных особенностей JavaScript

Истоки языка – обработка событий в браузере, «реактивность»

Нельзя совершать блокирующие действия

Значительно усложняет написание простых вещей

Page 38: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

38

Асинхронность

Page 39: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

39

Что же такое Promise<string> ? «Обещание» дать результат, в данном случае строчку

Результат будет получен асинхронно, прямо сейчас он недоступен

Вытащить значение можно передав в метод `.then` коллбэк, которому дадут результат по готовности

Аналог в Java: CompletableFuture<String>

Page 40: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

40

А почему работало? WebdriverJS скрывает от нас часть промисов с помощью Control

Flow

Page 41: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

41

А почему работало? WebdriverJS скрывает от нас часть промисов с помощью Control

Flow

Protractor добавляет поддержку промисов к проверкам (expect воспользуется Control Flow, если получит на вход промис)

Page 42: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

42

Паззлер №1: Чиним

let nick= listItem.all(by.tagName('td')).get(1).getText();let age = listItem.all(by.tagName('td')).get(2).getText();

let actualNickname = listItem .element(by.className('results-result')).getText();

Promise.all([nick, age]).then(function([nick, age]) { expect(actualNickname).toEqual(nick + (2017 - age));});

Page 43: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

43

Паззлер №1: Чиним

(async function() { doInputAndClick('Nick', '17'); let listItem = element.all(by.className('results-item')).get(0); let nick = listItem.all(by.tagName('td')).get(1).getText(); let age = listItem.all(by.tagName('td')).get(2).getText(); let actualNickname = listItem.element(by.className('results-result')).getText(); expect(actualNickname) .toEqual(await nick + (2017 - await age));})();

Page 44: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

44

Паззлер №2

Page 45: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

45

Паззлер №2

function doInputAndClick(nick, age) { element(by.css('.input-nick')).sendKeys(nick); console.log('INSERTED NICK'); element(by.id('MISTAKE')).click(); element(by.css('.input-age')).sendKeys(age); console.log('INSERTED AGE'); element(by.css('.input-submit')).click(); console.log('CLICKED THE BUTTON');}

Page 46: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

46

Паззлер №2

function doInputAndClick(nick, age) { element(by.css('.input-nick')).sendKeys(nick); console.log('INSERTED NICK'); element(by.id('MISTAKE')).click(); element(by.css('.input-age')).sendKeys(age); console.log('INSERTED AGE'); element(by.css('.input-submit')).click(); console.log('CLICKED THE BUTTON');}

Вариант 1: Ненайденные элементы игнорируются

Page 47: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

47

Паззлер №2

function doInputAndClick(nick, age) { element(by.css('.input-nick')).sendKeys(nick); console.log('INSERTED NICK'); element(by.id('MISTAKE')).click(); element(by.css('.input-age')).sendKeys(age); console.log('INSERTED AGE'); element(by.css('.input-submit')).click(); console.log('CLICKED THE BUTTON');}

Вариант 2: Вылетит исключение, увидим только первое сообщение в логе

Page 48: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

48

Паззлер №2

function doInputAndClick(nick, age) { element(by.css('.input-nick')).sendKeys(nick); console.log('INSERTED NICK'); element(by.id('MISTAKE')).click(); element(by.css('.input-age')).sendKeys(age); console.log('INSERTED AGE'); element(by.css('.input-submit')).click(); console.log('CLICKED THE BUTTON');}

Вариант 3: Вылетит исключение, но всё равно увидим все сообщения в логе

Page 49: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

49

Паззлер №2

function doInputAndClick(nick, age) { element(by.css('.input-nick')).sendKeys(nick); console.log('INSERTED NICK'); element(by.id('MISTAKE')).click(); element(by.css('.input-age')).sendKeys(age); console.log('INSERTED AGE'); element(by.css('.input-submit')).click(); console.log('CLICKED THE BUTTON');}

Вариант 4: Какой лог, мы только в Allure репорт писать можем!

Page 50: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

50

Паззлер №2function doInputAndClick(nick, age) { element(by.css('.input-nick')).sendKeys(nick); console.log('INSERTED NICK'); element(by.id('MISTAKE')).click(); element(by.css('.input-age')).sendKeys(age); console.log('INSERTED AGE'); element(by.css('.input-submit')).click(); console.log('CLICKED THE BUTTON');}

Вариант 1: Ненайденные элементы игнорируютсяВариант 2: Вылетит исключение, увидим только первое сообщение в логеВариант 3: Вылетит исключение, но всё равно увидим все сообщения в логеВариант 4: Какой лог, мы только в Allure репорт писать можем!

Page 51: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

51

Паззлер №2: Запускаем....

INSERTED NICKINSERTED AGECLICKED THE BUTTONFail!

Failures:1) Protractor Demo App should print correct logs Message: Failed: No element found using locator: By(css selector, *[id="MISTAKE"])

Page 52: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

52

Паззлер №2: Чиним

function log(text) { protractor.promise.controlFlow().execute( function () { console.log(text); } );}

Page 53: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

53

Паззлер №2

function doInputAndClick(nick, age) { element(by.css('.input-nick')).sendKeys(nick); log('INSERTED NICK'); element(by.id('MISTAKE')).click(); element(by.css('.input-age')).sendKeys(age); log('INSERTED AGE'); element(by.css('.input-submit')).click(); log('CLICKED THE BUTTON');}

Page 54: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

54

Итоги по асинхронности Сложно, но неизбежно

Используйте async/await по возможности (Node 7.6+)

В основе всё равно лежат промисы, про них надо знать

Пока что есть не у всех, поддержку control flow можно включать/выключать флагомpromise.USE_PROMISE_MANAGER = true/false

Больше информации тут: https://github.com/SeleniumHQ/selenium/issues/2969

Page 55: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

55

Паззлер №3

Page 56: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

56

Паззлер №3

it('should have correct title on login page', function() { element(by.linkText('Login')).click();

expect(browser.getTitle()).toEqual('Login page');});

Page 57: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

57

Паззлер №3

it('should have correct title on login page', function() { element(by.linkText('Login')).click();

expect(browser.getTitle()).toEqual('Login page');});

Вариант 1: Всё прекрасно, что тут может пойти не так???

Page 58: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

58

Паззлер №3

it('should have correct title on login page', function() { element(by.linkText('Login')).click();

expect(browser.getTitle()).toEqual('Login page');});

Вариант 2: linkText – нестандартный селектор, он не будет работать в Angular 4

Page 59: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

59

Паззлер №3

it('should have correct title on login page', function() { element(by.linkText('Login')).click();

expect(browser.getTitle()).toEqual('Login page');});

Вариант 3: Страница логина – не на Angular, Protractor не сможет на неё перейти

Page 60: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

60

Паззлер №3

it('should have correct title on login page', function() { element(by.linkText('Login')).click();

expect(browser.getTitle()).toEqual('Login page');});

Вариант 4: Страница логина – не на Angular, Protractor не сможет получить её заголовок

Page 61: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

61

Паззлер №3

it('should have correct title on login page', function() { element(by.linkText('Login')).click();

expect(browser.getTitle()).toEqual('Login page');});

Вариант 1: Всё прекрасно, что тут может пойти не так???Вариант 2: linkText – нестандартный селектор, он не будет работать в Angular 4Вариант 3: Страница логина – не на Angular, Protractor не сможет на неё перейтиВариант 4: Страница логина – не на Angular, Protractor не сможет получить её заголовок

Page 62: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

62

Паззлер №3: Запускаем....

StartedFail.Failures:1) Protractor Demo App should have correct title on login page Message: Failed: Error while waiting for Protractor to sync with the page: "window.angular is undefined."

Page 63: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

63

Паззлер №3: Чиним

it('should have correct title on login page', function() { element(by.linkText('Login')).click(); browser.ignoreSynchronization = true;

expect(browser.getTitle()).toEqual('Login page');});

Page 64: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

64

Итого

Page 65: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

65

Итого Protractor поможет написать тесты значительно быстрее

Но (в связке с JavaScript) может преподнести немало сюрпризов

Надо знать инструмент, которым пользуешься

Page 66: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

66

«У нас уже есть куча тестов на Java, да и страшно как-то после паззлеров писать на JS.

Что делать?»

Page 67: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

67

Вспомним тест на Java

@Testpublic void testResultAddedToHistory() throws Exception {

final List<WebElement> inputs = driver.findElements(By.tagName("input"));

inputs.get(0).sendKeys("Korobochka");inputs.get(1).sendKeys("17");driver.findElement(By.className("btn")).click();new WebDriverWait(driver, 10).until(webDriver ->

!webDriver.findElement(By.tagName("h3")).getText() .contains("Loading"));

WebElement targetElement = driver.findElement( By.xpath("//tbody/tr[1]/td[last()]"));

assertEquals(targetElement.getText(), "Korobochka2000");}

Page 68: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

68

Разбираемся с реализацией

browser.waitForAngular();

Page 69: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

69

Разбираемся ... (browser.js)

waitForAngular(opt_description) { if (this.ignoreSynchronization) { return true; } let runWaitForAngularScript = () => { return this.angularAppRoot().then((rootEl) => { return this.executeAsyncScript_( clientSideScripts.waitForAngular, rootEl); }); }; return runWaitForAngularScript()...

Page 70: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

70

Разбираемся ... (clientsidescripts.js)

/** * All scripts to be run on the client via executeAsyncScript or * executeScript should be put here. * * NOTE: These scripts are transmitted over the wire * as JavaScript text constructed using their toString * representation, and *cannot* reference external variables. */

Page 71: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

71

Разбираемся ... (clientsidescripts.js) waitForAngular

findBindings

findRepeaterRows

findAllRepeaterRows

findRepeaterElement

findByModel

findByOptions

findByButtonText

findByCssContainingText

testForAngular

Page 72: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

72

Разбираемся ... (clientsidescripts.js)functions.waitForAngular = function(rootSelector, callback) { try { if (window.angular && !(window.angular.version && window.angular.version.major > 1)) { ... } else if (rootSelector && window.getAngularTestability) { ... } else if (window.getAllAngularTestabilities) { ... } else if (!window.angular) { throw new Error('window.angular is undefined. ...') } } catch (err) { callback(err.message); }};

Page 73: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

73

Разбираемся ... (clientsidescripts.js)functions.waitForAngular = function(rootSelector, callback) { ... if (window.getAllAngularTestabilities) { var testabilities = window.getAllAngularTestabilities(); var count = testabilities.length; var decrement = function () { count--; if (count === 0) { callback(); } }; testabilities.forEach(function (testability) { testability.whenStable(decrement); }); } ...};

Page 74: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

74

Применяем в Java

Page 75: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

75

Итоги

Page 76: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

76

Используйте подходящий задаче инструмент Protractor очень хорошо подходит для тестирования Angular

приложений

Если есть возможность – используйте!

Page 77: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

77

Изучайте инструменты На примере выше мы подсмотрели, как работает Protractor, и

улучшили свои Java тесты

Так можно делать со многими вещами

Иногда даже имеет смысл написать свой инструмент

Page 78: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

78

Сложные программы и фреймворки должны предоставлять инструменты для тестирования

Тестирование – это не только поиск багов, но и сокращение расходов на разработку в целом

Даже небольшой вклад от разработчиков может удешевить процесс тестирования

Пользуйтесь API для тестирования, если они есть!

Page 79: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

79

Q&A

Илья Коробицын mailto: [email protected] Telegram: @korobochka

Код: https://github.com/korobochka/heisenbug-protractor

Page 80: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

80

Page 81: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

81

Backup slides

Page 82: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

82

Angular Testabilities

> getAllAngularTestabilities()[0].isStable();true

Page 83: Пишем Selenium тесты на JS для тестирования Angular: плюсы ... · Гейзенбаг, 2017-06-04 Илья Коробицын Пишем Selenium тесты

83

Universal waiter

public void waitForAnguar() {new WebDriverWait(driver, 10).until(webDriver ->

((JavascriptExecutor)webDriver).executeScript( "return getAllAngularTestabilities()0].isStable();" ).equals(Boolean.TRUE))}