В цій статті ми на простому прикладі розглянемо як можна покращити
Rails 5.1.3 System Test використовуючи Plain Old Ruby Objects,
collaborators, delegators і module.
Крок #0
Приклад до рефакторингу
Тест перевіряє три речі:
Чи можливо відкрити сторінку зі списком користувачів і чи має вона очікувану структуру
Чи можливо додати нового користувача і чи буде новий користувач на сторінці зі списком користувачів
Чи можливо оновити інформацію про користувача і чи будуть відображені зімни на сторінці зі списком користувачів
Крок #1
В цьому кроці ми:
Створимо новий абстрактний клас який в майбутньому допоможе нам описати структуру та функціонал HTML сторінок
Створимо page class для тестування сторінки з інформацією про користувача
Використаємо новий page class в тесті
Для початку ми додамо абстрактний клас, який має один метод для визначення елементів на сторінці, зміни можна переглянути у відповідному комміті
Давайте детальніше розглянемо метод initilaize та instance variables у ньому:
@current_session - за замовчуванням Capybara.current_session,
об’єкт-collaboratior що дозволяє нам використовувати driver всередині методу has_node
@url - обов’язкова змінна, URL сторінки що тестується
@css_wrapper - за замовчуванням порожня стрічка, допоміжний параметр, використовується коли всі елементи на сторінці знаходяться всередині елементу з певним CSS класом
Тепер додамо новий клас що описує сторінку з інформацією про користувача
Є три способи для визначення елементу на сторінці:
Я не додаватиму код нових класів тут, його можна знайти у відповідному комміті. Натомість давайте поглянемо на тест, що їх використовує:
У нас лишилось ще три кроки попереду проте давайте підсумуємо що ми вже отримали:
Ми використовуємо методи класу а не CSS/XPATH отож якщо структура сторінки зміниться ми повинні будемо змінити лише клас щоб виправити тести
Завдяки використанню collaborator objects код згрупований всередині блоків, його простіше зрозуміти і одразу очевидно на якій сторінці виконується кожна лінія коду
Крок #3
В цьому кроці ми:
Додамо можливість перевіряти чи присутній елемент всередині page classes
Додамо у Pages::Users::Show метод для перевірки структури сторінки
Метод Pages::Users::Show#check_main_elements_presence
Для отримання такого результату ми:
Змінили Pages::Base#initialize - тепер він очікує новий об’єкт-collaborator test:
Змінили Pages::Base#has_node - тепер він додає метод для доступу до елементу та перевірки наявності елементу на сторінці - *_present?
Крок #4
В цьому кроці ми вилучимо спільний функціонал у модуль (відповідний комміт)
Для початку порівняємо Pages::User::Edit та Pages::User::New
обидва мають однакові елементи first_name та last_name, що не дивно - ми render один і той самий partial form на обох сторінках. Окрім того ми заповнюємо цю форму коли тестуємо ці сторінки. Давайте вилучимо спільний функціонал у модуль.
Pages::Users::Partials::UserForm модуль
Page classes після рефакторингу
Крок #5
В цьому кроці ми:
Додамо можливість робити скріншот до page classes
Порівняємо як виглядав тест до Крок #1 та після Крок #5
Перша частина досить проста, оскільки ми вже маємо тест як об’єкт-collaborator
у Pages::Base нам лише потрібно додати take_screenshot до списку методів які ми делегуємо,
всі зміни можна переглянути у відповідному комміті
Тепер давайте порівняємо що ми мали на початку і як тест виглядає після рефакторингу
До
Після
версія ‘Після’ має певні переваги, ми перерахуємо їх у підсумку
Підсумок
Переваги OO підходу:
Тести менш ‘крихкі’ - якщо структура чи логіка сторінки зміниться досить буде змінити лише page class
Тести більш зрозумілі - завдяки використанню instance_eval та блоків завжди зрозуміло на якій сторінці ви знаходитесь
Значно простіше описати структуру сторінки
Однаковий функціонал можна помістити в модуль
Інші члени команди можуть використовувати готові page classes
Pages classes є POROs, Ви можете використовувати всю красу/потужність Ruby в них
Мені не подобається що Pages::Base має include Rails.application.routes.url_helpers. Це було зроблено лише щоб показати що статичний URL може бути частиною page class, має бути кращий спосіб
has_node працює лише з одним елементом, варто додати has_nodes для колекцій
В залежності від використаного фреймворку, методи делеговані в Pages::Base відрізнятимуться, проте його можна використовувати з іншими фреймворками (RSpec, …)
Замість багатьох тестів можна мати один супер-тест, тоді не доведеться чистити базу даних, можна групувати частини тесту за роллю користувача. Додаткові дані в базі можуть допомогти знайти глюки або лише ускладнити Ваше життя =)