Як написати ретельне, не крихке, одиничне тестування для графічного інтерфейсу?


16

Я спробував написати тести для інтерфейсу користувача для моїх програм GUI, і я стикаюся з проблемою, що, хоча вони спочатку їх пишуть, вони виявляються крихкими, і вони ламаються, коли дизайн змінюється (це досить часто). Я намагаюся знайти набір керівних принципів, які б привели мене до проведення ретельної одиничної тести для GUI.

Наразі одне, що я виявив, - це тести, які говорять про те, що "цей компонент повинен десь показувати вхідні дані" (і це дуже просто з HTML). Тести, що перевіряють конкретний стан конкретної частини компонента, як правило, крихкі. Тести, що йдуть як клік-натискання-очікування, які намагаються дотримуватися поведінки користувачів та основної логіки бізнесу (яка є найважливішою частиною), як правило, виявляються крихкими. Як написати хороші тести?


Якщо точніше, я хотів би знати деякі зразки про те, що я міг би перевірити у своєму інтерфейсі, а не як його перевірити. Іменування конвенцій та фіксованих ідентифікаторів є хорошими, але не вирішують основної проблеми, а саме те, що графічні інтерфейси сильно змінюються. Я хотів би перевірити поведінку, яка навряд чи зміниться. Як знайти потрібну річ для тестування?


1
@MichaelDurrant: Ви редагували питання про тестування інтерфейсу користувача взагалі, тоді як я спочатку запитував про тестування одиниць. Мені здається, що інтеграційні тести важче підтримувати, і я віддаю перевагу тестовим модулям над ними, коли це можливо.
mik01aj

2
Я думаю, що це є частиною проблеми, хоча, по суті, ви не можете реально перевірити будь-який інтерфейс, використовуючи тестування одиниці, оскільки їхня причина полягає в тому, щоб щось інтерфейсувати. У цьому відношенні графічні інтерфейси не відрізняються.
jk.

m01, ви можете змінити його назад. Я вважаю тести інтерфейсу як звичайні інтегровані тести. Тести, як правило, покладаються на наявні дані про насінні та кріплення та користувальницький інтерфейс, який працює з ними. Якщо у вас є справжні тести на інтерфейс користувача, які не покладаються на будь-які інші дані, які є чудовими. Однак я виявив це порівняно рідко.
Майкл Дюрант

2
пов'язаний, але не дублікат, оскільки мова йде про тести на gui: programmers.stackexchange.com/questions/109703/…
k3b

Відповіді:


3

Загальна проблема з тестами графічного інтерфейсу ... Основна причина, яку ці тести вважають крихкою, полягає в тому, що вони не можуть пережити зміну GUI, що не є зміною вимог . Ви повинні прагнути структурувати свій тестовий код таким чином, щоб зміна графічного інтерфейсу була виділена на одне місце в тестах.

Як приклад, розглянемо тест, який формулюється як:

Коли користувач вводить «999» у поле телефонного номера та натискає кнопку збереження, у спливаючому вікні повідомлення про помилку має бути сказано «недійсний номер телефону».

Тут достатньо місця для цього тесту, який вийде з ладу під час переробки інтерфейсу, навіть якщо вимога до перевірки залишається.

Тепер давайте вкажемо це на трохи альтернативне формулювання:

Коли користувач вводить номер 999 як номер телефону та зберігає сторінку профілю, він повинен відображати помилку, яка говорить про "недійсний номер телефону"

Тест такий самий, вимоги однакові, але цей вид тесту переживе макіяж вашого інтерфейсу. Очевидно, вам потрібно буде змінити код, але код буде ізольований. Навіть якщо у вас на сторінці профілю десять-двадцять таких тестів і ви переміщуєте логіку перевірки повідомлень про помилки з JavaScript-попереджень на jquery-спливаючі вікна, наприклад, вам потрібно лише змінити одну тестову частину, яка перевіряє повідомлення про помилки.


4

Це поширена проблема. Я зверну увагу на:

  • Як ви називаєте елементи

    Використовуйте css id або class для ідентифікації елементів. Користуйтеся використанням ідентифікатора CSS, коли об'єкт унікальний. Розглянемо рамку, яку ви використовуєте, наприклад, з Ruby on Rails nameатрибут призначається автоматично і може (не інтуїтивно) бути кращим, ніж використання ідентифікатора css або класу

  • Як ви визначаєте елементи.

    Уникайте позиційних ідентифікаторів, як table/tr/td/tdна користь таких форм, як td[id="main_vehicle"або td[class='alternates']. Подумайте про використання атрибутів даних, коли це доречно. Ще краще спробувати уникати тегів макета, таких як <td>цілком, щоб для вищевикладеного ви могли або додати проміжок, і використовувати це, наприклад, <span id="main_vehicle">або селекторний символ, наприклад, *[id="main_vehicle"]де *зараз може бути div, span, td тощо.

  • Використання специфічних атрибутів даних тесту , які використовуються лише для qa та тестування.

  • Уникайте зайвої кваліфікації елементів. Ви можете знайти себе за допомогою наступного:

    body.main div#vehicles > form#vehicle input#primary_vehicle_name

    Однак це вимагає, щоб поле введення залишалося у формі з точним ідентифікатором транспортного засобу та на сторінці з кузовом, який має клас основного та діл з ідентифікатором транспортних засобів, що має безпосереднє дочірнє оформлення форми з ідентифікатором транспортний засіб. Будь-яка зміна будь-якої з цієї структури і тест порушується. У цьому випадку ви можете це знайти

    input#primary_vehicle_name

    достатньо, щоб однозначно ідентифікувати елемент.

  • Уникайте тестів, які стосуються видимого тексту. Текст на сторінці, який відображається користувачеві, зазвичай змінюється з часом, оскільки сайт підтримується та оновлюється, тому використовуйте такі ідентифікатори, як css id та клас css або атрибути даних. Такі елементи, як form, inputі selectвикористовуються у формах, також є гарними частинами ідентифікаційних елементів, як правило, у поєднанні з id або класом, наприклад, li.vehicleабо input#first-vehicle Ви також можете додати власні ідентифікатори, наприклад <div data-vehicle='dodge'>. Таким чином ви можете уникнути використання ідентифікаторів елементів або класів, які, ймовірно, будуть змінені розробниками та дизайнерами. З часом я дійсно виявив, що краще просто співпрацювати з розробниками та дизайнерами та домовлятися щодо імен та обсягів. Це важко.

  • Як підтримуються фіксовані дані.

    Подібно до визначення фактичних елементів, намагайтеся уникати вбудованих твердо кодованих селекторів, що ідентифікують значення на користь об’єктів сторінки - невеликих фрагментів тексту, які зберігаються у змінних чи методах, і таким чином їх можна повторно використовувати, а також підтримувати централізовано. Приклади змінних javascript, що слідують за цим шаблоном для твердо кодованих значень:

    storedVars["eqv_auto_year"] = "2015";
    storedVars["eqv_auto_make_1"] = "ALFA ROMEO";
    storedVars["eqv_auto_make_2"] = "HONDA";`  
    

    Детальніше про об'єкти на сторінці у вікні селен та документи з селеном

  • Спілкування з розробниками.

    Незалежно від технічного підходу з точки зору "розробники вносять зміни та порушують автоматизацію якості", що є проблемою робочого процесу. Вам потрібно переконатися, що: всі - одна і та ж команда; розробник запускає ті ж інтегровані тести; стандарти узгоджуються та дотримуються обома групами; визначення виконаного включає запуск та можливо оновлення тестів інтерфейсу користувача; розробники та пара тестерів на тестових планах та обидва відвідують догляд за квитками (якщо вони роблять Agile) та розмовляють про тестування інтерфейсу користувача як частину догляду. Ви повинні переконатися, що будь-який підхід і стратегія, яку ви використовуєте для іменування, узгоджуються з розробниками додатків. Якщо ви не потрапляєте на одну і ту ж сторінку, вам сподобається зіткнення щодо іменування об'єктів. Деякі приклади методів об’єктів сторінки, які я нещодавно створив для рубінового проекту:

    def css_email_opt_in_true
      'auto_policy[email_opt_in][value=1]'
    end 
    
    def css_phone_opt_in
      '*[name="auto_policy[phone_opt_in]"]'
    end 
    
    def css_phone_opt_in_true
      'input[name=phone_opt_in][value=true]'
    end 
    
    def css_credit_rating
      'auto_policy[credit_rating]'
    end
    

    Ось такі самі об’єкти сторінки, що і змінні JavaScript:

    storedVars["css_email_opt_in"] = "css=*[name='auto_policy[email_opt_in]']";
    storedVars["css_phone_opt_in"]="css=*[name='auto_policy[phone_opt_in]']";
    storedVars["css_phone_opt_in_true"]="css=input[name='phone_opt_in'][value=true]";
    storedVars["css_credit_rating"]="css=select[name='auto_policy[credit_rating]']";
    

2
Це корисні підказки, і я фактично вже дотримуюся більшості з них. Моя проблема полягала в тому, що я пишу тести на якусь кнопку, а потім ця кнопка видаляється, коли ті ж дії виконуються з іншого місця. Або кнопка залишається там, але мітка змінюється, і кнопка виконує також додаткову дію.
mik01aj

1
Ах, це добре знати. Так, для цього я б зосередився на робочому процесі та взаємодії Qa-розробника
Майкл Дюррант,

1
Я також розміщую інформацію для тих, хто знайде ваше запитання і може не мати всіх частин на місці, які ви маєте або навіть не знаєте про них.
Майкл Дюрант

1
Я розширив свою останню точку відбитків, грунтуючись на ваших відгуках.
Майкл Дюрант

1
І я оновив частини про називання та ідентифікацію елементів та про невикористання видимого тексту.
Майкл Дюрант

3

Причиною, по якій люди розробляли такі речі, як MVC, MVP та презентатори спочатку, та подібні шаблони дизайну, було відокремлення бізнес-логіки від інтерфейсу користувача.

Очевидно, вигляд частини можна перевірити лише запустивши програму та перевіривши, що вона показує - іншими словами, її можна перевірити лише в тестах прийняття.

З іншого боку, тестування бізнес-логіки можна проводити в одиничних тестах. І це відповідь на ваше запитання. Перевірте все в моделі, і якщо ви можете і хочете, ви можете також протестувати код контролера.


Графічні інтерфейси сильно змінюються

Це може статися лише тоді, коли у вас змінилися вимоги. Коли вимога змінюється, її немає, крім зміни коду. Якщо вам вдасться створити хороший дизайн та архітектуру, зміна не пошириться у багатьох місцях.


2
Я думаю, що це вдалий момент. Можливо, я просто роблю MVC неправильно :) До речі, я вважаю, що зміни вимог неминучі, коли ви розробляєте веб-платформу для багатьох користувачів. Ви не знаєте, як будуть вести себе ваші користувачі, поки вони не почнуть використовувати графічний інтерфейс.
mik01aj

2

Тести взаємодії з графічним інтерфейсом повинні бути не більш-менш крихкими, ніж будь-які інші види тестів. Тобто; якщо ваша програма якимось чином змінюється, тести потрібно оновити, щоб це відобразити.

Як порівняння:

Тест одиниці

Оригінал : validateEmail()повинен кинути InvalidDataвиняток. Що правильно висвітлено у вашому тесті на одиницю.

Зміна : validateEmail()повинна кинути InvalidEmailвиняток. Тепер ваш тест невірний, ви оновите його, і все знову зелене.

GUI-тест

Оригінал : Введення невірного електронного листа призведе до появи спливаючого вікна з помилкою, що містить "Введені недійсні дані". Правильно виявлено вашими тестами.

Зміни : введення недійсного електронного листа призведе до вбудованої помилки, що містить "Недійсне введене повідомлення електронної пошти". Тепер ваш тест невірний, ви оновите його, і все знову зелене.


Пам'ятайте, що ви тестуєте на входи та виходи - певна чітко визначена поведінка. Незалежно від того, чи це тест на GUI чи тест на Unit або тест на інтеграцію тощо.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.