1 of 78

Flaky tests

Метод

Андрей Солнцев

twitter.com/asolntsev

asolntsev.github.io/ru/video/

2 of 78

В предыдущих сериях

“Flaky tests”

SeleniumCamp 2018

  • Примеры
  • Профилактика

“Flaky tests 2.0”

DelEx 2019

  • Анимация
  • Видосики

https://asolntsev.github.io/ru/video/

3 of 78

В предыдущих сериях

“Flaky tests”

SeleniumCamp 2018

  • Примеры
  • Профилактика

“Flaky tests 2.0”

DelEx 2019

  • Анимация
  • Видосики

Сегодня:

  • Порядок вещей
  • Метод

4 of 78

Flaky test

- это тест, который падает иногда

  • Тратите время!
  • Пропускаете реальные ошибки
  • Пропускаете usability issues
  • Теряется доверие к тестам

5 of 78

Ой, 30 тестов упало.�Надо изучить!

… Впрочем, �П……У….. !

6 of 78

Ой, 30 тестов упало.�Надо изучить!

… Впрочем, Перезапущу!

7 of 78

Каждый билд нужно изучать вручную

И вы называете это автоматизацией?

8 of 78

Индустрия в опасности!

9 of 78

Порядок

10 of 78

Как вы это протестируете?

  1. Клик “Сохранить”�
  2. Надпись “Сохраняю…”�
  3. Клик “Ок”�
  4. Надпись пропадает�

11 of 78

Тест:

  • “Сохранить”�
  • “Сохраняю…”�
  • “Ок”�
  • Пропадает

$(“.save”).click();�

$(“.saving”).should(appear);�

$(“.ok”).click();

$(“.saving”).should(disappear)

12 of 78

Тест:

  • “Сохранить”�
  • “Сохраняю…”�
  • “Ок”�
  • Пропадает

$(“.save”).click();�

$(“.saving”).should(appear);�

$(“.ok”).click();

$(“.saving”).should(disappear)

  • Автоматические ожидания
  • Решает 90% флейки тестов

selenide.org

13 of 78

Усложняем задачу

  • Клик “Сохранить”�
  • Надпись “Сохраняю…” (N секунд)�
  • Надпись пропадает САМА�

14 of 78

Тест:

  • “Сохранить”�
  • “Сохраняю…”�
  • Пропадает

$(“.save”).click();�

$(“.saving”).should(appear);�

$(“.saving”).should(disappear)

15 of 78

  • “Сохранить”�
  • “Сохраняю…”�
  • Пропадает

$(“.save”).click();�

$(“.saving”).should(appear);�

$(“.saving”).should(disappear)

Что может пойти не так?

16 of 78

Вариант 1: шустрый браузер

  • “Сохранить”
  • “Сохраняю…”
  • Пропадает

$(“.save”).click();

$(“.saving”).should(appear);

$(“.saving”).should(disappear)

FAILED

17 of 78

Вариант 2: тормознутый браузер

1) “Сохранить”

$(“.save”).click();

$(“.saving”).should(appear);

$(“.saving”).should(disappear)

2) “Сохраняю…”

> 4 сек

3) Пропадает

FAILED

18 of 78

Компромисс 1

  • “Сохранить”
  • “Сохраняю…”
  • Пропадает

$(“.save”).click();�

$(“.saving”).should(appear);�

$(“.saving”).should(disappear)

FAILED

Идём на компромисс.

Отказываемся от этой проверки.

19 of 78

Компромисс 2

  • “Сохранить”����
  • “Сохраняю…”�
  • Пропадает

$(“.save”).click();�

$(“.saving”).should(appear);�

$(“.saving”).should(disappear)

НЕ НАХОДИТ БАГУ

Идём на компромисс.

Отказываемся от этой проверки.

20 of 78

Итак,�как надёжно проверить

кейс

“Сохранить”?

21 of 78

Как надёжно проверить�кейс “Сохранить”?

Правильный ответ:

НИКАК!

(без хитрых хаков)

22 of 78

Почувствуй разницу:

  1. Клик “Сохранить”�
  2. Надпись “Сохраняю…”�
  3. Проходит время
  4. Надпись пропадает�
  • Клик “Сохранить”�
  • Надпись “Сохраняю…”�
  • Клик “Ок”�
  • Надпись пропадает�

Мы контролируем

Мы НЕ контролируем

23 of 78

Почувствуй разницу:

  • Клик “Сохранить”�
  • Надпись “Сохраняю…”�
  • Проходит время
  • Надпись пропадает�
  • Клик “Сохранить”�
  • Надпись “Сохраняю…”�
  • Клик “Ок”�
  • Надпись пропадает�

Тест надёжный

Тест ненадёжный

24 of 78

Порядок в тесте:

  • Act
  • Assert�
  • Act
  • Assert�...
  • Клик “Сохранить”�
  • Надпись “Сохраняю…”�
  • Клик “Ок”�
  • Надпись пропадает�

25 of 78

Порядок в тестах

  1. Act
  2. Assert
  3. Assert
  4. Assert
  • Act
  • Assert
  • Act
  • Assert
  • ...

Этот принцип - на каждом шагу!

26 of 78

Пример:

Автосохранение

видео

27 of 78

Логи AUT и теста:

10:59:12,865 UITest click.before <a>1 Contact and Addresses</a>

10:59:13,121 UITest click.before <button>Start filling</button>

10:59:13,686 UITest click.before <button>Forward</button>

10:59:13,851 UITest click.before <a>5 Preview</a>

10:59:13,905 request POST /loanapplications/mortgage/save ...

10:59:14,010 UITest click.after <a>5 Preview</a>

28 of 78

Когда тест зелёный:

Когда тест красный:

click.before <a>1 Contact and Addresses</a>

click.before <button>Start filling</button>

click.before <button>Forward</button>

click.before <a>5 Preview</a>

POST /loanapplications/mortgage/save ...

click.after <a>5 Preview</a>

click.before <a>5 Preview</a>

click.after <a>5 Preview</a>

POST /loanapplications/mortgage/save ...

29 of 78

Хорошая идея -

совместить логи

теста и приложения

30 of 78

Но как же решить�нашу задачу�с автосохранением?

31 of 78

Как-то контролировать порядок!

  1. Клик “страница 1”
  2. Дождаться автосохранения
  3. Клик “страница 2”
  4. Дождаться автосохранения
  5. ...

Но как?

32 of 78

Как дождаться автосохранения?

  1. Надпись “сохраняю…”
  2. Иконка типа “дискета”
  3. Через JavaScript “jQuery.active”

Нет.

Всё это мы не контролируем.

33 of 78

В AUT: добавляем признак

function autosaveDraft() {

self.form.removeClass('autosaved');

self.form.addClass('autosaving');

$.post(...)� .then(() => {

self.form.addClass('autosaved');� })

34 of 78

В тесте: проверяем признак

// заполняем форму, кликаем “Дальше”

$("#form").shouldHave(cssClass("autosaving"));

$("#form").shouldHave(cssClass("autosaved"));

А вдруг он остался от �предыдущего автосохранения?

35 of 78

В тесте: проверяем признак

// Arrange:

executeJavaScript("document.getElementById('form').classList.remove('autosaving')");

// Act: заполняем форму, кликаем “Дальше”

$("#form").shouldHave(cssClass("autosaving"));

$("#form").shouldHave(cssClass("autosaved"));

36 of 78

Unit-test

UI test

  • Arrange
  • Act
  • Assert
  • Arrange
  • Act
  • Assert
  • Arrange
  • Act
  • Assert
  • ...

AAA - �на каждом шаге!

Шаг 1

Шаг 2

37 of 78

https://seleniumcamp.com/talk/arrange-mazafaka/

38 of 78

Arrange!

Перед каждым шагом:

  1. А всё ли готово?
  2. А всё ли закончилось на предыдущем шаге?
  3. А обнулилось ли состояние?
  4. А не грузится ли там в углу какая-нибудь предыдущая хреновина?

39 of 78

Хочешь поймать �флейки тест �-

думай как флейки тест!

40 of 78

Пример:

Три-четыре

видео

41 of 78

Есть flaky тест

$$("#devices .device").shouldHave(size(4));

  • В 99.9% запусков он зелёный.
  • Но иногда падает.
  • Потому, что элементов не 4, а 3.

42 of 78

Изредка тест ломается:

List size mismatch: expected: = 4, actual: 3

Elements: [

<tr class="device">BlackBerry</tr>,

<tr class="device">Lumia</tr>,

<tr class="device">HTC</tr>

]

43 of 78

Смотрим тест вдумчиво:

$(".remove-device").click();

$$("#devices .device").shouldHave(size(4));

$$("#devices .device").shouldHave(

texts("BlackBerry", "Lumia", "HTC")

);

44 of 78

Смотрим тест вдумчиво:

$(".remove-device").click();

$$("#devices .device").shouldHave(size(4));

$$("#devices .device").shouldHave(

texts("BlackBerry", "Lumia", "HTC")

);

Почему строк 4, но текста 3?

45 of 78

Исправленный тест:

$$("#devices .device").shouldHave(size(4));

$(".remove-device").click();

$$("#devices .device").shouldHave(size(3));

$$("#devices .device").shouldHave(

texts("BlackBerry", "Lumia", "HTC")

);

46 of 78

Пример:

Ожидается -1 строк

47 of 78

Правильный тест (вроде бы?)

@Test

public void closeDeposit() {

fastLogin("/deposits");

int openDeposits = $$("#deposits tbody tr").size();

// bla-bla-bla закрыть депозит

$$("#deposits tbody tr").shouldHave(size(openDeposits - 1));

}

48 of 78

Правильный тест (вроде бы?)

@Test

public void closeDeposit() {

fastLogin("/deposits");

int openDeposits = $$("#deposits tbody tr").size();

// bla-bla-bla закрыть депозит

$$("#deposits tbody tr").shouldHave(

size(openDeposits - 1)

);

}

ARRANGE

ACT

ASSERT

49 of 78

Но иногда он падает:

List size mismatch: expected: = -1, actual: 3, collection: #deposits tbody tr

Elements: [

<tr>73456 810 0 3331 0007779 Topupable 1 year 2.35% 25.04.2020 Active</tr>,

<tr>Deferred Deferred 6 months 3.67% 01.07.2017 Deferred</tr>,

<tr>Deferred Deferred 6 months 3.67% 01.07.2017 Deferred</tr>

]

Их и правда 3:

Но тогда что это за хрень?

50 of 78

Это халтура, а не ARRANGE!

@Test

public void closeDeposit() {

fastLogin("/deposits");

int openDeposits = $$("#deposits tbody tr").size();

// bla-bla-bla закрыть депозит

$$("#deposits tbody tr").shouldHave(

size(openDeposits - 1)

);

}

ARRANGE

Список ещё не успел подгрузиться

51 of 78

Когда тестируешь UI�-

ты не можешь �доверять UI

52 of 78

Вот правильный ARRANGE:

@Test

public void closeDeposit() {

fastLogin("/deposits");

// bla-bla-bla закрыть депозит

$$("#deposits tbody tr").shouldHave(

size(openDeposits - 1)

);

}

ARRANGE

int openDeposits = DB.select(“count(*) from deposits”);

$$("#deposits tbody tr").shouldHave(size(openDeposits));

size(openDeposits - 1)

53 of 78

Вот правильный ARRANGE:

@Test

public void closeDeposit() {

fastLogin("/deposits");

int openDeposits = DB.select(“count(*) from deposits”);

$$("#deposits tbody tr").shouldHave(size(openDeposits));

  • Доверенный источник
    • Test data
    • SQL
    • API
    • Что угодно
  • только НЕ ЧЕРЕЗ UI

54 of 78

Пример:

Сотри за мной нежно

55 of 78

Тест ок

Тест failed

Кнопка есть. Ок.

Кнопки нет, потому что �эта штука уже активирована

56 of 78

Версия

Осталось �от предыдущего теста?

57 of 78

Как подчищаются данные:

@After

public void deleteRegistration() {

if ($("#turnoff").is(visible)) {

$("#turnoff").click();

}

}

58 of 78

Предыдущий тест:

| ...

|.alert-success |should have(text 'done successfully') |PASS |

|#sbp-turnoff-confirmation-button |is(visible) |PASS |

|#sbp-turnoff-confirmation-button |click() |PASS |

|#sbp-turnoff-button |click() |PASS |

+---------------------------------+----------------------+----------+----------|

| ...

|.alert-success |should have(text 'done successfully') |PASS |

|#sbp-turnoff-confirmation-button |is(visible) |PASS |

+---------------------------------+----------------------+----------+----------|

А когда наш тест упал:

59 of 78

Неправильно:

Правильно:

@After

public void delete() {

...

}

@Before

public void delete() {

...

}

60 of 78

Неправильно:

Правильно:

@After

public void delete() {

if ($(...)

.is(visible)) {

...

}

}

@Before

public void delete() {

DB.executeSQL(

“delete from orders”

);

}

  • Доверенный способ
    • SQL
    • API
    • Что угодно
  • только НЕ ЧЕРЕЗ UI

61 of 78

И наконец,

62 of 78

Метод (изучения флейки теста)

Море гипотез

63 of 78

Метод (изучения флейки теста)

Море гипотез

Гипотеза 1

делим море пополам

64 of 78

Метод (изучения флейки теста)

Море гипотез

65 of 78

Метод (изучения флейки теста)

Море гипотез

Гипотеза 2

Гипотеза 3

Гипотеза 4

и т.д.

  1. Как эта хрень выглядит на практике?
  2. Как выбрать гипотезу?

66 of 78

Метод (изучения флейки теста)

  1. Создать ветку в GIT / джобу в CI
  2. Гонять только этот тест 1000 раз
  3. Проверить гипотезу
  4. Повторять шаги 2-3 (пока не позеленеет)

67 of 78

Пример

  1. Клик “заказать карту”
  2. Браузер зависает

org.openqa.selenium.TimeoutException:

timeout: Timed out receiving message from renderer: 10.000

68 of 78

69 of 78

Проверяем гипотезу

  • Поменял картинки на маленькие (1KB) -
  • билд не упал ни разу за два дня:

Гипотеза:

размер (картинок)�имеет значение

70 of 78

Проверяем гипотезу

  • Добавил 10x больших картинок -
  • билд стал падать чаще:

Похоже, правда!

71 of 78

Вылечил флейки тест

  • Отрефакторил страницу,
  • чтобы все картинки не грузились сразу.
  • + сжал маленькие картинки
  • Тест больше не падает

72 of 78

Значит,

Он упадёт завтра!

73 of 78

Следить за тестами -�Это как печь зефир

74 of 78

Следить за тестами -�Это как печь зефир

Нужно следить постоянно,�Иначе может подгореть

75 of 78

Следить за тестами -�Это как печь зефир

Нужно следить постоянно,�Иначе может подгореть

Нужно много времени�И терпения

76 of 78

Следить за тестами -�Это как печь зефир

Нужно следить постоянно,�Иначе может подгореть

Нужно много времени�И терпения

77 of 78

Фигачьте�вдумчиво

78 of 78

Андрей Солнцев

@asolntsev

asolntsev.github.io

ru.selenide.org