Watir WebDriver как средство автоматизации тестирования

    Недавняя статья про WebDriver напомнила мне немного рассказать об используемом нами комплексе для автоматизации тестирования Web-приложений.

    Итак, в основе тестов лежит лежит связка RSpec + Watir WebDriver (для Rails-приложений более уместно будет взглянуть в сторону Capybara). При поддержке Bundler и локальных WebDrivers осуществляется лёгкая инсталляция на рабочем месте тестировщика (установить Ruby 1.9, Rubygems, Bundler, и выполнить bundle install). Исходник Gemfile:

    source :rubygems
    
    gem "watir-webdriver", "~>0.6.1"
    gem "rspec-core", "~> 2.0"
    gem "rspec-expectations", "~> 2.0"
    gem "rr", "~> 1.0"
    gem "ci_reporter", "~> 1.7"
    


    За счет гема ci_reporter осуществляется интеграция с Jenkins CI, а за счёт гема parallel_tests и Selenium Grid распараллеливание тестов (на сегодня parallel tests пока не используются в production).

    Вот пример теста:

    describe "FirstSite" do
      before(:all) do
        site! "first"
      end
    
      # returns true on success; otherwise false.
      def login_as(data)
        browser.rel_goto "/"
        browser.title.should include 'example.com'
        browser.text_field(:id => 'login').set data[:login]
        browser.text_field(:id => 'password').set data[:password]
        submit_button = browser.button(:id => 'submit')
        submit_button.click
        browser.url =~ %r{/welcome$}
      end
    
      def logout
        browser.rel_goto "/"
        browser.button(:id => 'logout').click
      end
    
      describe :login do
        auth = fixture :auth
    
        describe :successful do
          after(:each) do
            logout
          end
    
          auth.keys.select { |key| key.kind_of?(Symbol) }.each do |key|
            next  if key == :wrong
    
            it "Logging in as #{key} should be OK" do
              login_as(auth[key]).should be_true
            end
          end
        end
    
        describe :wrong do
          it "Logging in with wrong credentials should fail" do
            login_as(auth[:wrong]).should_not be_true
          end
        end
      end
    end
    
    


    Тестируемые сайты и способ тестирования определяются в настройках.
    ---
    production:
      sites:
        first: "http://staging.example.com"
      webdriver:
        hub: "http://192.168.13.6:8888/wd/hub"
    test:
      sites:
        first: "http://staging.example.com"
      webdriver:
        hub: "http://192.168.13.6:8888/wd/hub"
    development:
      sites:
        first: "localhost:5678"
      webdriver:
        local: true
    


    За счёт задания переменных окружения WATIR_ENV и WATIR_BROWSER мы определяем окружение тестирования для конкретного прогона тестов.

    Вся кастомизация выполняется двумя файлами. .rspec:

    --format nested
    --color
    --require ./spec_helper.rb


    spec_helper.rb:

    require "rubygems"
    require "bundler/setup"
    require "yaml"
    require "watir-webdriver"
    require "rspec/core"
    
    $LOAD_PATH << File.join(File.dirname(__FILE__), 'lib') # для различных библиотек.
    
    module RegruHelpers
      include Selenium
    
      def site!(site)
          RSpec.configuration.site = RSpec.configuration.sites[site]
      end
    
      def fixture(name)
        RegruHelpers.symbolized YAML.load_file(
          File.join(File.dirname(__FILE__), 'fixtures', "#{name}.yml")
        )
      end
    
      def screenshot!(text = '')
        directory = File.join(File.dirname(__FILE__), 'spec', 'screenshots')
        file_name = "#{example.metadata.full_description}-#{text}.png"
        file_path =File.join(directory, file_name)
        browser.screenshot.save file_path
      end
    
      def save_html!(text = '')
        directory = File.join(File.dirname(__FILE__), 'spec', 'html')
        file_name = "#{example.metadata.full_description}-#{text}.html"
        file_path =File.join(directory, file_name)
        File.open(file_path, "w") do |f|
          f.print browser.html
        end
      end
    
      Watir::Browser.class_eval do
        def rel_goto url
          goto "#{RSpec.configuration.site}#{url}"
        end
      end
    
      def browser
        RSpec.configuration.browser ||= begin
          local = RSpec.configuration.watir_settings[:webdriver][:local]
          if local
            Watir::Browser.new(RSpec.configuration.browser_type)
          else
            capabilities = WebDriver::Remote::Capabilities.send(
              RSpec.configuration.browser_type,
              :javascript_enabled => true
            )
            Watir::Browser.new(
              :remote, 
              :url => RSpec.configuration.watir_settings[:webdriver][:hub], 
              :desired_capabilities => capabilities
            )
          end
        end
      end
    end  
    
    RSpec.configure do |config|
      config.mock_with :rr
    
      config.add_setting :watir_settings
      config.add_setting :browser_type
      config.add_setting :sites
      config.add_setting :site
      config.add_setting :browser
    
      config.include RegruHelpers
      config.extend RegruHelpers
    
      environment = ENV['WATIR_ENV'] || 'test'
      browser_type = (ENV['WATIR_BROWSER'] || 'chrome').to_sym
      watir_settings = RegruHelpers.symbolized YAML.load_file(File.join(File.dirname(__FILE__), 'settings.yml'))[environment]
      RSpec.configuration.watir_settings = watir_settings
      RSpec.configuration.sites = watir_settings[:sites]
      RSpec.configuration.site = watir_settings[:sites]['first']
      RSpec.configuration.browser_type = browser_type
    
      config.after(:suite) do
        unless RSpec.configuration.browser.nil?
          RSpec.configuration.browser.close
          RSpec.configuration.browser = nil
        end
      end
    
      config.after(:each) do
        if example.exception
          screenshot!('after')
          save_html!('after')
        end
      end
    
      def self.symbolized(hash)
        r = hash.dup
        hash.each_pair do |k, v|
          if k.kind_of?(String)
            if v.kind_of?(Hash)
              v = symbolized v
            end
            r[k.to_sym] = v
          end
        end
        r
      end
    
    end
    
    REG.RU
    Каждый день мы делаем Рунет лучше! Вы с нами?
    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 14

      0
      akzhan, спасибо за статью, очень интересно. А не могли бы вы немного подробнее описать технические детали, ну или хотя бы указать направление где такое описание есть? Интересует настройка Grid + Selenium Server (ы). Как то не так давно игрался с Selenium, но так и не удалить настроить Grid — только обычный selenium-server-standalone с запуском тестов в один поток. Правда использовался phpUnit у которого с многопоточностью есть проблемы. Ну и сами настройки сервера тоже интересны, в частности что используется для запуска браузеров, снятия скриншотов и т.п.
      0
      Эм, скриншоты и остальное всё через WebDriver. В коде же есть.

      Selenium Grid настроен почти стандартно, завтра опишу в комментариях. Надо сказать, изначально Грид был настроен Александром Хаёровым, а я лишь стабилизировал это дело, используя runit.
        0
        Скажем так — далеко не на всех серверах стоят иксы, поэтому я например использовал xvfb. Хотелось услышать как вся эта кухня устроена у вас. :)
        0
        Через Xvfb.
          0
          А тесты работают параллельно? Это не мешает снятию скринов?
            0
            Используются же несколько инстансов, сейчас вот готовим узел на WXP.
              0
              Вот про это было бы интересно почитать. Потому что статей как писать тесты — полно. А вот про то как все правильно настроить что то не пишут.
            0
            А почему серверы не на Windows? Все же сборки браузеров разные, лучше тестировать на целевой операционной системе
              0
              Уже Grid на Windows, со вчерашнего дня.
            0
            В первоначальном коде была ошибка. вместо examle.metadata.exception следует писать example.exception

            Следствие непутёвой правки живых тестов :)
              +1
              А мы используем Watirsplash — это обёртка вокруг Watir, позволяющая писать более короткие/читабельные тесты благодаря тому, что вам не нужно заботиться об объекте browser. Вот пример теста на Watirsplash:

              # spec\app\page\search_spec.rb
              describe App::Page::Search do
                it "has something on the search page" do
                  search_page = App::Page::Search.new
                  search_page.search_field.set "watirsplash"
                  results_page = search_page.search_button.click
                  results_page.result(1).should =~ /watirsplash/i
                end
              end
                0
                Спасибо, смотрю. Возможно, как минимум, надо browser определить как subject.
                0
                За исходники, конечно, спасибо, но хотелось бы более тезисного описания где здесь watir, а где — Selenium. И, соответственно, в чем преимущества использования именно этой связки по сравнению с тем же selenium-webdriver.

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