Тестирование Rails приложения, используя PageObject

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


О подходе

PageObject - это паттерн проектирования, используемый в тестах для создания "прослойки" отделяющей общую логику тестирования от частных элементов DOM'a. Грубо говоря, "объект-страница" знает, что у неё есть форма с кнопкой "сохранить", и есть метод, позволяющий эту кнопу нажать. А реализация поиска элемента кнопки будет скрыта в самом классе страницы, предоставляя, таким образом, высокоуровневый интерфейс к управлению страницой. Это позволяет более гибко подходить к разработке тестов и упрощает дальнейшее поддержание их в актуальном состоянии.


От слов к делу

Придумаем простую задачу: на странице /user/edit есть форма с именем юзера:

<form>
  <input type="text" class="username" name="username" />
  <input type="submit" class="submit" value="save" />
</form>

После сохранения ожидается следующее поведение: в блоке с классом "message" выводится сообщение с текстом - "Success".


Реализация

Для тестирования будем использовать, конечно же, Rspec и Capybara.

Класс объекта страницы с реализацией методов заполнения имени, сохранения формы и получения текста сообщения:

class UserEditPage
  def initialize(page)
    @page = page
  end
 
  def message_text
    page.find('.message').try(:text)
  end
 
  def set_name(username)
    page.fill_in 'username', with: username
  end
 
  def save_changes
    page.click_button 'save'
  end
end

Теперь можно использовать этот класс для управления страницой:

it 'should be able to save username' do
  visit '/user/edit'
 
  current_page = UserEditPage.new(page)
 
  current_page.set_name('Омар')
  current_page.save_changes
 
  expect(current_page.message_text).to eq("Success")
end

Так как страниц в одном сценарии тестирования может быть много, для удобства можно добавить фабрику, которая бы относила текущую страницу определённому классу, по какому-то уникальному ключу, например URL'у.

Для начала, завернём, всё что относится к страницам в модуль Pages.

Теперь добавим метод, определяющий уникальность страницы в класс UserEditPage:

def self.matches?(page)
  !!(page.current_path =~ /^\/user\/edit/)
end

И сама фабрика:

module PageFactory
  def current_page
    current_page_class = (Pages.constants).
                          map  { |e| Pages.const_get(e) }.
                          find { |e| e.matches?(page) }
    current_page_class.new(page)
  end
end

Теперь в любом месте можно использовать метод current_page.


19.03.2015
Обсуждение недоступно