Upload
tim-vroom
View
288
Download
12
Embed Size (px)
Citation preview
Testing Your Website With Selenium (and Perl)
Tim Vroom - [email protected] YAPC NA - June 2015 Salt Lake City, Utah
Photo by waynerd on flickr
About Me
Tim Vroom Once upon a time was creator
of perlmonks.org
Currently work at slashdot.org
@vroomtim on twitter
GitHub Repo
• https://github.com/tvroom/selenium-testing-with-perl
Short url of ( http://tinyurl.com/sel-perl )
(slides / example code / web pages / scripts) — final updates may come in next few days
What is Selenium?
• A tool for web-browser automation & testing
• Allows for functional testing that also runs JavaScript loaded on the page
• Can run tests against multiple web browsers
Why might you want to use it?
Test close to where users experience your code (in the browser)
Test things your JS, and CSS are responsible for, layout in browser, visibility of messages etc…
Speed up involved debugging / testing
while developing long flows
Don’t spend your time typing and clicking to test code as you develop, in complicated code, and flows.
Automate the tedium of testing… so you can write more code, and test your code changes more quickly.
What we’ll cover
• Running your first Selenium test with Perl
• SeleniumRC vs Selenium Webdriver
• Key Perl libraries to consider using for your tests
• Locating Elements & Handling Waits
• Variety of Example Tests
What we’ll finish up with… Time in the Selenium Playground working through listed test tasks
Steps to running your first Selenium Test with Perl
1. Download Selenium Server - selenium-server-standalone-2.45.0.jar (latest currently) at http://www.seleniumhq.org/download/
2. Run the server:
3. Install Selenium::Remote::Driver from CPAN
java -jar selenium-server-standalone-2.45.0.jar
Sample YAPC Testuse Selenium::Remote::Driver; use Selenium::Remote::WDKeys; use Test::More;
my $driver = Selenium::Remote::Driver->new(default_finder => 'css');
$driver->get('http://www.google.com');
$driver->find_element('q','name')->send_keys('YAPC Salt Lake City',KEYS->{'enter'});
$driver->set_implicit_wait_timeout(5000);
$driver->find_element('#rso li h3 a')->click(); my $title = $driver->get_title(); like($title, qr/YAPC/, "Title for YAPC page is what is expected");
$driver->find_element('Talks and Schedule', 'link_text')->click(); $driver->find_element('Schedule','link_text')->click(); $driver->find_element('Wednesday','link_text')->click(); $driver->find_element('Selenium','partial_link_text')->click();
$title = $driver->get_title(); like($title, qr/Selenium/, "Title seems to be about testing with Selenium"); sleep 5; # For demo purposes wait a bit before closing so we can see the final page $driver->quit();
done_testing();
Versions of Selenium
Remote Control - Vs - WebDriver
Selenium RC (Selenium 1)
• Selenium Remote Control serves as middle layer between code and browser
• When you run a test it pops one window for the Remote Control, and then that window operates your test window via javascript
• Operates browser actions by running Javascript to mimic those actions
• Due to translation in middle layer sometimes did things differently than the browser actually would
Your Code -> Selenium RC —> Browser test is running in
Selenium 2 (WebDriver API)
• Talks directly to browser via WebDriver API calls
• Browser makers have built WebDriver support right into the browser
• Typically faster running tests due to more direct nature
• Behaves more consistently with actual browser behavior, because you’re executing native code with-in the browser, which matches up with WebDriver API Calls
Additional Reading on JsonWireProtocol (or WebDriver wire protocol)
• JsonWireProtocol - learn about the protocol (and capabilities provided) in the Protocol used to power WebDriver
• https://code.google.com/p/selenium/wiki/JsonWireProtocol
• Particularly - the command reference section: https://code.google.com/p/selenium/wiki/JsonWireProtocol#Command_Reference
SELENIUM VERSION REMOTE CONTROL (RC) WEBDRIVER
BASE PERL MODULE WWW:::Selenium Selenium::Remote::Driver
TEST MODULE Test::WWW::Selenium Test::Selenium::Remote::Driver
OTHER STUFF WWW::Selenium::NetworkCaptureWWW::Selenium::Util
Selenium::FirefoxSelenium::ChromeSelenium::PhantomJS
Perl Modules By Selenium Version
• Selenium::Firefox • Selenium::Chrome • Selenium::PhantomJS
New modules in the space - run WebDriver tests w/o Selenium Standalone Server
(from Selenium::Remote::Driver docs)
Sample code for this presentation
• Will primarily show code interacting with Selenium Webdriver (Selenium::Remote::Driver Perl Module)
• Repo will contain examples with SeleniumRC in most cases for comparison
Typical Test FlowStep 0. Find a Thing in the DOM
Step 1. Do something to that thing, or look at its attributes
Step 2. Wait appropriate amount of time if necessary before doing next thing
Repeat…
Key Concepts While Writing Tests
Use Dev Tools in Chrome or similar to inspect the element you’re interested in
and find a selector for it
Methods to Find Your Elements
find_elementfind_element($search_string, $locator_type);
# find using css locator — type$driver->find_element(‘h3.articlehead’,’css’);
# use default of path for locator$driver->find_element(‘//*[@id=“logo”]/a’);
If your element is not found — will CROAK, killing your script. (unless you check for and trap in try/catch or eval block etc)
Returns Selenium::Remote::WebElement object
How to override default_finder on driver initialization
my $driver = Selenium::Remote::Driver->new(default_finder => ‘css');
# default to css now instead of path $driver->find_element(‘h3.article’);
Convenience methodsfind_element_by_class
find_element_by_class_name
find_element_by_css
find_element_by_id
find_element_by_link
find_element_by_link_text
find_element_by_name
find_element_by_partial_link_text
find_element_by_tag_name
find_element_by_xpath
find_elementsmy $ar = $driver->find_elements($search,$opt_locator);
Same as find_element, but returns an array, or array ref
Warning — latest Selenium::Remote::Driver does not croak now on find_elements with size of 0 (none found)
— but may in the future
Strategies for testing for elements that aren’t there
• find_element (trap croak / eval / try/catch) • find_elements?? (0 size array(ref), or croak pay
attention to what your version does)
• Test::Selenium::RemoteDriver method $driver-
>find_no_element_ok($search_target [,$finder,
$desc ]); • use JavaScript $driver->execute_script(‘return
your_test_for_no_element()’);
css # <div class=“header”></div> $driver->find_element(‘.header’,’css);
Locate Via…
name # <input type=“text” name=“user” value=“” /> $driver->find_element(‘user’,’name’);
link_text # <a href=“/schedule.html”>Schedule</a> $driver->find_element(‘Schedule’,’link_text’);
xpath # <div id=“logo”><a href=“/”></div> $driver->find_element(‘//*[@id=“logo"]/a’,’xpath’);and more… class, class_name, id, link, partial_link_text, tag_name
find_element methods return (on success)
Selenium::Remote::WebElement objects
Selenium::Remote::WebElement defines methods to take action on the elements or test attributes of them, look at the documentation to see what’s available
This allows you to chain calls like: $driver->find_element(‘.foo’,’css’)->click(); $driver-find_element(‘username’,’name’)->send_keys(‘bob’);
Selenium::Remote::WebElement
click submit send_keys is_selected set_selected toggle is_enabled get_element_location get_element_location_in_view get_tag_name
clear get_attribute get_value is_displayed is_hidden drag get_size get_text get_css_attribute describe
Selenium::Remote::WebElement Methods
Managing Waits For Your
Tests
WaitsTests need to properly wait for completion of
actions, before looking for an element provided as a result of previous action
Load a new page
Change current page via Ajax or JS
When you….
Too short/ Non-Existent = failing tests
Too long = (long sleeps), slow test suites
Just right - wait only as long as you need to = passing tests + fast test suites
Range of Waits
Selenium::Remote::Driver waits for new page loads
Default Behavior:polls and waits for up to ‘page load’ timeout (default 180 secs)
You can change this timeout via:
$driver->set_timeout('page load', 10000); # 10 second timeout in ms
Controls actions that take you to a new page. ie: navigate to a new page, full page form submit
Selenium::Remote::Driver waits for in page element
changes
poll and wait up to implicit_wait_timeout for find_element - default is 0 ms
You can change this timeout via:
$driver->set_implicit_wait_timeout(5000) # change to 5 secs in ms
Selenium::Remote::Driver (wait for condition)
Selenium RC has a method, which runs a piece of JS checking for a condition for up to a timeout threshold
# wait until a new article is inserted $sel->wait_for_condition(‘window.$(".article").length >’.$orig_count, 10000);
You could write your own method to extend Selenium::Remote::Driver to provide the same functionality (sample of this in repo)
Example Tests
These will cover some tests that go beyond navigating pages / links / and
filling out forms
Taking a Screenshot
use Selenium::Remote::Driver;
my $driver = Selenium::Remote::Driver->new(); $driver->get("http://blogs.perl.org"); $driver->capture_screenshot("screenshot.png"); $driver->quit();
Running JS in your tests$driver->execute_script() method to run JS and return
resultsuse Selenium::Remote::Driver; use Test::More; my $driver = Selenium::Remote::Driver->new(); $driver->navigate("http://timvroom.com/selenium/js_testing.html"); my $shopping_count = $driver->execute_script( 'return $(".shopping-list li").length' ); ok($shopping_count >= 3, "Shopping list has at least 3 items");
done_testing();
Verify JS library inclusionuse Selenium::Remote::Driver;use Test::More;
my $driver = Selenium::Remote::Driver->new();
$driver->navigate("http://timvroom.com/selenium/js_testing.html");
ok(js_defined("window.jQuery"), "jQuery installed on $_[0]");ok(js_defined("window.jQuery.cookie"), "jQuery cookie plugin installed");ok(js_defined("window.Handlebars"), "has Handlebars installed");
done_testing();
sub js_defined { my $val = shift; my $ret = $driver->execute_script("return !(typeof $val === 'undefined')"); return $ret;}
Capturing Network Data
WWW::Selenium::NetworkCapture - gives you a way of seeing all the HTTP requests, and response information generated by your Selenium
session
Webdriver doesn’t offer this capability, but you could run your tests through a webproxy, and then analyze results
Ex) Test for missing failed resources loading (Source HTML)
<html> <head> <title>Broken resources test page</title> </head> <body> <h1>Broken resources test page</h1> <style type="text/css"> #logo { background:transparent url(/missing_logo_call_from_css.png) no-repeat top left; float:left; height: 20px; width: 120px; } </style> <div id="logo"></div> <img src="broken_image.png" alt="broken_image"> <script src="broken_script.js" type="text/javascript"> </body> </html>
use WWW::Selenium;use WWW::Selenium::NetworkCapture;use Data::Dumper;use Test::More;
my $sel=WWW::Selenium->new( browser_url => "http://timvroom.com/selenium/ga_test.html");$sel->start();$sel->{session_id} = $sel->get_string( "getNewBrowserSession", $sel->{browser_start_command}, $sel->{browser_url}, undef, 'captureNetworkTraffic=true');
$sel->open("http://timvroom.com/selenium/broken_resources.html");$sel->wait_for_page_to_load(10000);
my $traffic_xml = $sel->get_string('captureNetworkTraffic', 'xml');
my $netcap = WWW::Selenium::NetworkCapture->new($traffic_xml);my @stat_code_urls = map { [$_->{statusCode}, $_->{url}]} grep { $_->{url} !~ /favicon.ico/ } grep { $_->{statusCode} =~/^(4|5)/ } @{$netcap->{dom}{entry}};
ok(!@stat_code_urls, "Resources loaded w/o 40x or 50x errors for $key $url\n") or diag Dumper(@stat_code_urls);$sel->stop();done_testing();
Test Script
not ok 1 - Resources loaded w/o 40x or 50x errors for## Failed test 'Resources loaded w/o 40x or 50x errors for# '# at missing_resources.t line 27.# $VAR1 = [# [# '404',# 'http://timvroom.com/selenium/broken_image.png'# ],# [# '404',# 'http://timvroom.com/selenium/broken_script.js'# ],# [# '404',# 'http://timvroom.com/missing_logo_call_from_css.png'# ]# ];1..1# Looks like you failed 1 test of 1.
Results
use WWW::Selenium;use WWW::Selenium::NetworkCapture;use Data::Dumper;use Test::More;
my $sel=WWW::Selenium->new( browser_url => "http://timvroom.com");$sel->start();$sel->{session_id} = $sel->get_string( "getNewBrowserSession", $sel->{browser_start_command}, $sel->{browser_url}, undef, 'captureNetworkTraffic=true');
$sel->open("http://timvroom.com/selenium/ga_test.html");$sel->wait_for_page_to_load(10000);
my $traffic_xml = $sel->get_string('captureNetworkTraffic', 'xml');
my $netcap = WWW::Selenium::NetworkCapture->new($traffic_xml);
my @requests = @{$netcap->{dom}{entry}};
my @ga_track_pv = grep { $_->{url} =~ m|www.google-analytics.com/r/collect\?.*t=pageview| } @requests;my @non_200_ga_track = grep { !$_->{statusCode} == 200 } @ga_track_pv; ok(@ga_track_pv == 1, "Got exactly 1 GA track pageview request");ok(!@non_200_ga_track, "All GA track pageview requests got a 200 statusCode");
Check for Google Analytics Tracking
Results:
ok 1 - Got exactly 1 GA track pageview request ok 2 - All GA track pageview requests got a 200 statusCode 1..2
Now let’s hit the
Selenium Playground!!!
Remind us again what this fun thing is? This is a static page with automation tasks you can run your
automated script against and then check the results. Hopefully a good way to get your feet wet with web
automation
Start with run_playground_empty.pl from the repo which will load the page and check results (which gives us lots
of test fails)
( http://tinyurl.com/selplay )
Navigate to:
http://tinyurl.com/selplayon your phone or computer if you want to give input on the tasks from the list we demo in the
presentation
(I’ll ask for suggestions — raise your hand and call out a number you’d like to see demo-ed once I call on you)
If you want to see the script that solves in action run… run_playground_answers.pl (or
check answers if you’re stuck on a task)
Questions? Talk repo at:
https://github.com/tvroom/selenium-testing-with-perl
Short url of ( http://tinyurl.com/sel-perl )
Thanks for your time!
and now a short Public Service Announcement….
Take a few minutes and provide your feedback - so we can learn what worked, what didn’t and how to improve
and probably all other speakers would like your HONEST feedback on the online speaker evaluations.
(Check your email for instructions)
This friendly guy…..
Thanks for listening
Talk repo at: https://github.com/tvroom/selenium-testing-with-perl
Short url of ( http://tinyurl.com/sel-perl )