What is Selenium?
Java | Python | Perl | PHP | …
↓↑
Selenium API
↓↑
Browser
# Underlying mechanism:
# run arbitrary JavaScript
# in main browser UI thread
driver.execute_script('…')
# Send and receive strings, numbers,
# lists, dicts, DOM elements.
value1, value2, value3 = driver.execute_script(
'…', arg1, arg2, arg3,
)
# Python API
driver.get('https://2016.pycon.ca/en/')
# (Selenium method names are too long!)
find = driver.find_element_by_css_selector
h2 = find('h2')
assert 'Toronto' in h2.text
nav = find('.navigation__link')
nav.click()
Selenium tests are expensive
Selenium tests are expensive
Selenium tests are:
find = driver.find_elements_by_css_selector
find('.navigation__link').click()
assert driver.find('h1').text == 'Schedule'
#
#
#
find = driver.find_elements_by_css_selector
find('.navigation__link').click()
assert driver.find('h1').text == 'Schedule'
# Broken!
# Might grab <h1> on current page
# Might select before <h1> arrives
“But the test passes on my laptop!”
“It’s probably just a flake.”
(your branch)
──diff── × ──diff── ✓ ──land
│
│
│
(master) ─ ✓ ── ✓ ── ✓ ── ? ── ── ── ── ──
(your branch)
──diff── × ──diff── ✓ ──land
╲
✓ rebase + retest
╲
(master) ─ ✓ ── ✓ ── ✓ ── ✓ ── ? ── ── ── ──
(your branch)
──diff── × ──diff── ✓ ──land
╲
✓ rebase + retest
╲
(master) ─ ✓ ── ✓ ── ✓ ── ✓ ── ✓ ── ✓ ── ✓ ── ✓ ──
pass-fail-pass
A test in Quarantine:
Damage of false red
≫
damage false green
January 2016
(test_time ÷ n) + (setup_time × n)
driver.window_handles
driver.switch_to_window(handle)
Dispose of the VM
=== TEST START ===
...
==== TEST END ====
=== TEST START ===
Traceback (most recent call last):
...
Exception: ...
==== TEST END ====
Traceback (most recent call last):
...
Exception: ...
Service halted; restarting
Service halted; restarting
=== TEST START ===
Traceback (most recent call last):
...
Exception: ...
==== TEST END ====
Use only one browser
Avoid raw Selenium
Test API
Wait for elements
Timeouts everywhere
Use your site like a human—
# No
find('.navigation__link').click()
find('._sd_login_button').click()
# Yes
find_link('Schedule').click()
find_button('Login').click()
# Turns out? This is N round-trips!
[button.text == 'Login'
for button in find('button')]
https://github.com/elliterate/capybara.py
page.visit("/")
page.click_link("Sign in")
page.fill_in("Username", value="user@example.com")
page.fill_in("Password", value="password")
page.click_button("Sign in")
combinations of n gates
Provide:
for setting gates
class NormalTests(unittest.TestCase):
def ...
def ...
def ...
def ...
class ExperimentTests(NormalTests):
def setUp(self):
gate.set('use_new_hash', True)
Capybara + Armor + Scale
=
Capybara + Armor + Scale
=
Capybara + Armor + Scale
=
Thank you! — @brandon_rhodes