Testowanie Cucumberem dla wielu użytkowników – część dalsza
Trochę ponad miesiąc temu pisałem o testach aplikacji typu real-time za pomocą Cucumbera. Minęło trochę czasu i wydana została nowa wersja – teraz ze wsparciem dla Capybary. Jako że wcześniejsza metoda sprawiała dość dużo problemów przy przenoszeniu z systemu na system(hacki pod Snow Leoparda czasami nie działały w innych systemach) więc warto przyjrzeć się nowemu rozwiązaniu – tym razem działającemu OOTB na każdym systemie.
Ponownie nie będę się rozwodził nad instalacją i konfiguracją(jest to opisane na stronie Capybary) więc przejdźmy od razu do zastosowania w praktyce. Capybara domyślnie nie umożliwia otwarcia wielu instancji przeglądarki, a zapytany o tą funkcjonalność główny programista pluginu odpowiada że “it’s impossible by design”. Cóż powiedzieć – nie ma rzeczy niemożliwych, a Polak potrafi ;) Po przekopaniu kodu Capybary(akurat tu muszę pochwalić bardzo dużą czytelność) znalazłem parę punktów “podporu” dzięki którym można obejść zabezpieczenia twórcy przed otwarciem paru okienek przeglądarki.
Zacznijmy więc od początku. W features/step_definitions tworzymy sobie nowy zestaw kroków – powiedzmy “selenium_steps.rb”. Na początek przyjmijmy że chcemy otworzyć 2 przeglądarki – na razie nam to wystarczy, a jeśli w przyszłości chcieli byśmy testować więcej to bardzo łatwo to zmienić. Tworzymy sobie więc metodę sprawdzającą czy istnieją już instancje przeglądarek i (jeśli nie) tworzącą je:
def check_selenium_browsers Capybara.instance_variable_set('@current_driver', :selenium) if $browsers.nil? $browsers = {} current_url $browsers[1] = get_selenium_browser set_selenium_browser(nil) current_url $browsers[2] = get_selenium_browser set_selenium_browser(1) end end
Druga linijka służy do zapewnienia, że używamy prawidłowego silnika testów(w naszym przypadku jest to selenium) Wywołanie metody “current_url” gwarantuje nam, że przed przejściem dalej stan przeglądarki zostanie zapisany i nie ulegnie zmianie. Dalej pobieramy i zapisujemy w zmiennej aktualny stan przeglądarki po czym go zerujemy aby Capybara otworzyła następną. Dlaczego samemu tego nie robimy? Otóż Capybara używa dość sporo tricków powodujących że ciężko by było dostosować się przyszłych refaktoryzacji kodu – ta metoda całkowicie wystarcza i gwarantuje nam że przez dłuższy czas wszystko będzie działało stabilnie. Następne wywołanie “current_url” otwiera nam już drugą przeglądarkę itd. Na koniec ustawiamy jako domyślną przeglądarkę początkowy stan – dzięki temu stan wejścia i wyjścia jest identyczny.
Przejdźmy więc do implementacji funkcji pobierających i zapisujących stan przeglądarki.
def get_selenium_browser { :session => Capybara.current_session, :driver => Capybara::Driver::Selenium.instance_variable_get('@driver') } end
Jak widać ta metoda jest banalna – pobiera ona jedynie obecną sesję(dla Selenium) oraz “driver”(czyli tak naprawdę referencję do instancji serwera Selenium).
def set_selenium_browser(browser_id) browser = $browsers[browser_id] || {} if browser[:session].nil? Capybara.instance_variable_set('@session_pool', {}) Capybara::Driver::Selenium.instance_variable_set('@driver', nil) else Capybara.instance_variable_set('@session_pool', {"selenium#{Capybara.app.object_id}" => browser[:session]}) Capybara::Driver::Selenium.instance_variable_set('@driver', browser[:driver]) end end
Tu też nie ma żadnej magii – zapisujemy stan sesji i podmieniamy referencję do serwera Selenium. Tak zapisany kod pozwala nam na łatwe zapisanie funkcji Cucumbera zmieniających przeglądarki:
Given /^I am using first browser$/ do check_selenium_browsers set_selenium_browser(1) end Given /^I am using second browser$/ do check_selenium_browsers set_selenium_browser(2) end
Możemy w ten sposób używać dowolnej ilości przeglądarek i testować w nich nie tylko Juggernauta, ale i WebSockets czy inne funkcjonalności(nawet zwykłą, ajaxową komunikację między użytkownikami – zaoszczędzi to nam czasu przy przelogowywaniu się pomiędzy requestami).
Na koniec tylko dodam, że przy wychodzeniu z Cucumbera nie wszystkie instancje przeglądarki zostaną zamknięte(a dokładnie – zamknięta zostanie tylko obecnie aktywna) Aby “posprzątać” po sobie warto dodać w którymś z plików w inicjalizacyjnych(features/support) kod:
at_exit do $browsers.each { |id, browser| browser[:driver].quit } rescue nil end
Rocznik 1986, miłośnik linuksa i dobrej muzyki. Ostatnio nawrócony na Mac-a ze względów czysto praktycznych. Wesoły, towarzyski i pracowity ;)