Professional Documents
Culture Documents
Wade Catron
1
Agenda
• Background
• Core Technology Choices
• Barebones Test Framework
• Introducing Pageness
• Cool Tricks
• Supporting Infrastructure
• Wrapup & Questions
Background 2
Background
Background 3
LinkedIn Web Apps
• Java based web applications
• Complex social interactions
• Users of all shapes and sizes
• Variations of common flows
• Hundreds of pages and states
• Plenty of rich, gooey ajaxiness
Background 4
LinkedIn SDLC
• Very frequent product enhancements
• New features developed in parallel
• 10+ feature branches per release
• Rapid dev/test/release cycle
• Tons of testing environments, some with
disparate data sets
Background 5
LinkedIn QA
• QA staff embedded in dev teams
• Expert domain knowledge
• New feature/release validation
• Regression testing of existing product
• Owns thousands of test specs
• Strong scripting skillz
Background 6
QA Automation Goals
• Automate everything, early
• Produce long lived test assets
• Share code between teams
• Strive for minimal maintenance costs
• Author tests with the quickness
• Balance “easy” vs “flexible/powerful”
Background 7
Core Technology
• Test::Unit
– Simple, known, xUnit pattern
– Extensible
– One script per test
Barebones Framework 10
Test::Unit Example
Class SignInTest < Test::Unit::TestCase
def setup
@env = Environment.new
end
def test_sign_in
@env.selenium.open(@env.base_url)
@env.selenium.type(“id=session-email”,
@env.main_user.email)
...
...
end
end
Barebones Framework 11
Ruby Selenium Client Driver
• Simple, verb oriented syntax
• Support for ajax operations
• Well maintained
Barebones Framework 12
Selenium Client Example
sel.click_and_wait(”//id('main')/div/div[2]/dl[1]/dt[5]/a")
sel.click("id=CONNECTIONS-statusSettingsParam-statusSettings”)
sel.click_and_wait(“//input[@class='btn-primary']”)
assert(sel.is_element_present("//div[@class='alert succ']"))
Barebones Framework 13
Selenium Client Example
Barebones Framework 14
Fundamental Problems
• Hardcoded locators
– brittle tests
– bad for readability
– failure/error analysis is tedious!
• Driver dependent code
Barebones Framework 15
Pageness
Introducing Pageness 16
We Need Driver Abstraction!
Introducing Pageness 17
Desired Outcome
• Shared locators
• Cleaner, more readable scripts
• More idiomatic, noun based API
privacy_page = PrivacySettingsPage.new(@env)
privacy_page.connections_only_radio_option.click
privacy_page.save_button.click_and_wait
settings_page.success_message.assert_present
Introducing Pageness 18
Describing A Page
end
Introducing Pageness 19
The Magical Page Class
sign_in_page = SignInPage.new(@env)
def password_field
return Element.new(:password_field, ‘session-passwd_login’)
end
def sign_in_button
return Element.new(:sign_in_button, ‘session_login’)
end
Introducing Pageness 20
What’s an Element?
sign_in_page.email_field
Element Object
@name = :email_field
@locator = ‘session-key_login’
Introducing Pageness 21
Element Specific Interface
sign_in_page.email_field.type(“email@linkedin.com”)
Element Object
@name = :email_field
@locator = ‘session-key_login’
Introducing Pageness 22
Element Specific Interface
sign_in_page.email_field.type(“email@linkedin.com”)
Element Object
@name = :email_field
@locator = ‘session-key_login’
Introducing Pageness 23
Element Specific Interface
sign_in_page.email_field.type(“email@linkedin.com”)
Element Object
@name = :email_field
@locator = ‘session-key_login’
Introducing Pageness 24
Element Specific Interface
sign_in_page.email_field.type(“email@linkedin.com”)
Element Object
@name = :email_field
@locator = ‘session-key_login’
…
@sel.type(‘session-key_login’, ‘email@linkedin.com’)
Introducing Pageness 25
End Result
• Shared locators
• Cleaner, more readable test scripts
• More idiomatic, noun based API
settings_page = SettingsPage.new(@env)
settings_page.privacy_settings_link.click_and_wait
privacy_page = PrivacySettingsPage.new(@env)
privacy_page.connections_only_radio_option.click
privacy_page.save_button.click_and_wait
settings_page.success_message.assert_present
Introducing Pageness 26
Another Example
# Sign in to the app
sign_in_page = SignInPage.new(@env)
sign_in_page.email_field.type(email)
sign_in_page.password_field.type(email)
sign_in_page.login_button.click_and_wait
Background 28
Build Custom Methods
class Element
def assert_present
assert(self.is_element_present)
end
def click_if_present
self.click if self.is_element_present
end
def type_each_key(string)
string.each_byte do |byte|
char = byte.chr
@sel.key_down(locator, char)
@sel.key_press(locator, char)
@sel.key_up(locator, char)
end
end
Cool Tricks 29
Loop over all Elements
HomePage class definition
class HomePage < Page
end
Test script
home_page = HomePage.new(@env)
home_page.elements.each do |element|
element.assert_present
end
Cool Tricks 30
Contexual Locators
Example: Iterating through a table or list to collect results
<ul id="messages">
<li>
<h1>Invitation to reconnect</h1>
<p>It's been too long, man!..</p>
</li>
<li>
<h1>Chunky Bacon</h1>
<p>so good</p>
</li>
...
</ul>
index = 1
locator = “//ul[@id='messages']/li[#{index}]/h1”
while @sel.is_element_present(locator)
results << @sel.get_text(locator)
index += 1
locator = “//ul[@id='messages']/li[#{index}]/h1”
end
Cool Tricks 31
A Pagier Solution
element :subject_by_index,
Proc.new { |index| "//ul[@id=‘messages’]/li[#{index}]” }
inbox_page = InboxPage.new(@env)
messages = []
count = 1
while inbox_page.subject_by_index.is_element_present(:index => count) do
messages << inbox_page.subject_by_index.get_text(:index => count)
count += 1
end
assert_equal(messages.length, 10)
assert_true(messages.include? “Welcome to LinkedIn”)
Cool Tricks 32
Supporting Infrastructure
Wrapup 33
Integrated Bits
• Test helper and utility classes
• Multi-threaded test runner for parallel
execution
• Environmental data classes for urls/credentials
• Custom test class properties: priority,
grouping, requires_isolation
• HTTP based data generator integration
Supporting Infrastructure 34
A Few Statistics
• Adopted page model 6 months ago
• Wrote 887 test scripts between 5 teams
• Daily average of 28 test-related commits
• Average number of elements per page: 18
• 250+ defined pages
Shama Butala-Katkar
Boris Roussev
Helen Shyu
Priyanka Salvi
Background 36
?
wcatron@linkedin.com
Questions 37