Page Object — путь к совершенным автотестам



    Всем, доброго времени суток!
    Этот топик о том как мы тестируем веб-интерфейс нашего продукта Plus1 WapStart. Мы используем Page Object, т.к. этот паттерн имеет много общего с реальными задачами и позволяет писать автотесты простыми для чтения и понимания.

    Что такое Page Object


    Page Object — это паттерн для реализации умных автоматических проверок. Gem page-object является имплементацией этого паттерна, который помогает в создании гибких страниц с объектами для тестирования браузерных приложений. Суть в том, чтобы создавать уровни абстракции для отделения тестов от предметов тестирования, и обеспечить простой интерфейс для элементов на странице. Gem работает с watir-webdriver и selenium-webdriver.

    Установка Ruby и gems


    Установим RVM:
    $ bash -s stable < <(curl -s https://raw.github.com/wayneeseguin/rvm/master/binscripts/rvm-installer)

    Для запуска примеров понадобиться последняя версия Ruby и gems:
    $ rvm reload
    $ rvm install ruby-1.9.3-p125
    $ rvm gemset create page-object && rvm use ruby-1.9.3-p125@page-object
    $ gem install rake page-object
    

    Hello, Page Object!


    Создаем RegistrationPage для страницы регистрации — для этого включаем модуль PageObject, указываем адрес страницы и описываем элементы:

    class RegistrationPage
       include PageObject
    
       page_url "http://www.passport.wapstart.ru/registration/"
    
       text_field(:email, :name => 'mail')
       text_field(:password, :id => 'hidden-password')
       select_list(:source, :name => 'informationSource')
       checkbox(:agreement, :name => 'agreement')
       button(:register, :name => "register")
    end

    На странице описаны шесть методов, каждый из которых дополнительно сгенерирует еще несколько, об этом далее… Добавляем данные по умолчанию:

    DEFAULT_DATA = {
       'email' => 'habrahabr@gmail.com',
       'password' => 'qwerty',
       'source' => 'Прочитал статью, новость о WapStart' 		
     }


    Создаем метод для заполнения только обязательных полей:

    def default(data = {})
       populate_page_with DEFAULT_DATA.merge(data)
       check_agreement
       register
    end


    Все базовые page objects определены, используем их в автотесте. Выберем драйвер selenium-webdriver, передав его в конструктор PageObject:

    browser = Selenium::WebDriver.for :ff
    registration_page = RegistrationPage.new(browser, true)
    registration_page.default

    Аргумент true открывает page_url, если мы попадаем на эту страницу кликом по ссылке, то его можно не передавать. Запустим пример:

    $ ruby tests/RegisterDefault.rb

    Таким образом в автотестах уходим от использования find_element и локаторов, код легко читаемый и повторно используемый.

    Динамическая генерация методов доступа к элементам страницы


    Accessors — методы класса, добавленные на страницу, подключением модуля PageObject. Методы генерируют еще один набор методов, которые обеспечивают доступ к элементам веб-страницы:


    Пример полностью описанной страницы регистрации:
    require 'page-object'
    require 'selenium/webdriver'
    
    class RegistrationPage
       include PageObject
    
       page_url "http://www.passport.wapstart.ru/registration/"
    
       DEFAULT_DATA = {
    	'email' => 'habrahabr@gmail.com',
    	'password' => 'qwerty',
    	'source' => 'Прочитал статью, новость о WapStart' 		
       }
    
       text_field(:email, :name => 'mail')
       text_field(:password, :id => 'hidden-password')
       checkbox(:showPassword, :id => 'showPassword')
       select_list(:source, :name => 'informationSource')
       text_field(:fio, :name => 'fio')
       text_field(:phone, :name => 'phone')
       select_list(:purpose, :name => 'registrationPurpose')
       checkbox(:haveCode, :id => 'havePartnerCode')
       text_field(:code, :id => 'plus1PartnerCode')
       checkbox(:agreement, :name => 'agreement')
       button(:register, :name => "register")
    
       # Exception
       span(:errorCode, :xpath => "//form[@id='registrationForm']/table/tbody/tr[9]/td[2]/span/span[2]")
    
       def default(data = {})
           populate_page_with DEFAULT_DATA.merge(data)
           check_agreement
           register
       end
    end

    Пример автотеста для проверки валидности партнерского кода:
    describe "RegisterUser" do
       let(:browser) { @browser ||= Selenium::WebDriver.for :ff }  #Initialize new instance of Browser(driver)
    
       it "InvalidCode" do
           page = RegistrationPage.new(browser, true)
           page.email = 'habrahabr@gmail.com'
           page.password = 'qwerty'
           page.source = 'Прочитал статью, новость о WapStart'
           page.check_haveCode
           page.code = 12345678
           page.check_agreement
           page.register
           page.errorCode?.should be_true
       end
    
       after { browser.close }
    end

    Примеры доступны на GitHub:
    $ git clone git@github.com:ivaravko/pageobject-example.git
    $ cd pageobject-example/
    pageobject-example$ rake

    Открытие страницы из другой страницы


    В большом проекте много страниц и они связаны между собой, для того чтобы выполнять переход добавим следующий шаг в default метод класса RegistrationPage — LoginPage.new(browser, true). Это позволит перейти на страницу ввода логина и пароля, не изменяя автотест.
    def default(data = {})
       populate_page_with DEFAULT_DATA.merge(data)
       check_agreement
       register
       LoginPage.new(browser, true)
    end

    Автор топика ivaravko

    Ссылки

    Блог автора гема
    Страница на GitHub
    Документация
    • +16
    • 49.7k
    • 8
    WapStart
    27.41
    Company
    Share post

    Comments 8

      +1
      Спасибо. Правда мне Capybara за глаза.
        0
        Интересно. Еще бы пример с ajax посмотреть.
          +2
          C Ajax все просто, нужно ожидать пока выполняется загрузка:
          @page.wait_until(5, "Call not returned within 5 seconds") do
            @page.text.include? "Value returned from Ajax call"
          end

          Подробнее на вики github.com/cheezy/page-object/wiki/Ajax-Calls
          –1
          А для тех кто не знает руби — можно больше теории?
            +1
            Для этого нужно писать еще один топик. Посмотрите мастр-класс Николая Алименкова, там есть теория и практика: часть 1 и часть 2.
              –1
              Интересует конкретно паттерн.
                0
                Статья Фаулера
                  0
                  Не прошло и двух лет :) Хотя постойте-ка…

          Only users with full accounts can post comments. Log in, please.