Python Selenium:

Fundamentals to Frameworks

(with SeleniumBase)




Presented by Michael Mintz

SeleniumConf 2023 - Chicago

Welcome to Chicago!


A few shout-outs before starting:



  • The Selenium Org (made everything possible)

  • Conference organizers (made today possible)

  • My wife (a major supporter of my work)

  • iboss (my employer)

About me:

  • I created the SeleniumBase framework.
  • I lead the Automation Team at iboss.

This is the ONLY Python session at SeleniumConf!



If one Python session is not enough, come see me afterwards.

By the end of this presentation, you'll learn:



  • Python Selenium fundamentals.

  • How test frameworks can improve on the fundamentals.

  • How SeleniumBase makes Python Web Automation easier.

The Format:


  • Slides.
  • ReadMe files.
  • LOTS of live demos!!!

What does Selenium provide?



Selenium provides an automation library!


(It does NOT provide a test framework.)

How do you get Selenium?


(for Python)


pip install selenium

What are some building blocks?



from selenium import webdriver

driver = webdriver.Chrome()

driver.get("http://selenium.dev")

element = driver.find_element("css selector", "#docsearch span")

element.click()

elem_2 = driver.find_element("css selector", "#docsearch-input")

elem_2.send_keys("Python")

driver.quit()

What are some building blocks?



from selenium import webdriver

driver = webdriver.Chrome()

driver.get("http://selenium.dev")

element = driver.find_element("css selector", "#docsearch span")

element.click()

elem_2 = driver.find_element("css selector", "#docsearch-input")

elem_2.send_keys("Python")

driver.quit()

What are some building blocks?



from selenium import webdriver

driver = webdriver.Chrome()

driver.get("http://selenium.dev")

element = driver.find_element("css selector", "#docsearch span")

element.click()

elem_2 = driver.find_element("css selector", "#docsearch-input")

elem_2.send_keys("Python")

driver.quit()

What are some building blocks?



from selenium import webdriver

driver = webdriver.Chrome()

driver.get("http://selenium.dev")

element = driver.find_element("css selector", "#docsearch span")

element.click()

elem_2 = driver.find_element("css selector", "#docsearch-input")

elem_2.send_keys("Python")

driver.quit()

What are some building blocks?



from selenium import webdriver

driver = webdriver.Chrome()

driver.get("http://selenium.dev")

element = driver.find_element("css selector", "#docsearch span")

element.click()

elem_2 = driver.find_element("css selector", "#docsearch-input")

elem_2.send_keys("Python")

driver.quit()

What are some building blocks?



from selenium import webdriver

driver = webdriver.Chrome()

driver.get("http://selenium.dev")

element = driver.find_element("css selector", "#docsearch span")

element.click()

elem_2 = driver.find_element("css selector", "#docsearch-input")

elem_2.send_keys("Python")

driver.quit()

Launching a browser can get more complicated:



from selenium import webdriver

options = webdriver.ChromeOptions()
options.add_argument("--disable-notifications")
options.add_experimental_option(
    "excludeSwitches", ["enable-automation", "enable-logging"]
)
prefs = {
    "credentials_enable_service": False,
    "profile.password_manager_enabled": False,
}
options.add_experimental_option("prefs", prefs)
driver = webdriver.Chrome(options=options)

Test frameworks wrap Selenium to improve things!



(Where does a framework fit in?)

What are some disadvantages of using raw Selenium without additional libraries or frameworks?



What are some disadvantages of using raw Selenium without additional libraries or frameworks?


The default timeout is 0: If an element isn't immediately ready to be interacted with, you'll get errors when trying to interact with those elements.

What are some disadvantages of using raw Selenium without additional libraries or frameworks?



The command statements can get a bit too long:

driver.find_element(By.CSS_SELECTOR, CSS_SELECTOR).click()


This is better:

self.click(CSS_SELECTOR)


What are some disadvantages of using raw Selenium without additional libraries or frameworks?



The command statements can get a bit too long:

driver.find_element(By.CSS_SELECTOR, CSS_SELECTOR).click()


This is better:

self.click(CSS_SELECTOR)


What are some disadvantages of using raw Selenium without additional libraries or frameworks?



No HTML reports, dashboards, results, screenshots...


A test framework can provide those!

Raw Selenium disadvantages, continued...

No HTML reports, dashboards, results, screenshots...
A test framework can provide those!

Raw Selenium disadvantages, continued...



It takes multiple lines of code to do simple tasks:

element = driver.find_element("css selector", "#password")
element.clear()
element.send_keys("secret_sauce")
element.submit()

But with a framework, do all that in ONE line:

self.type("#password", "secret_sauce\n")

Raw Selenium disadvantages, continued...



It takes multiple lines of code to do simple tasks:

element = driver.find_element("css selector", "#password")
element.clear()
element.send_keys("secret_sauce")
element.submit()

But with a framework, do all that in ONE line:

self.type("#password", "secret_sauce\n")

What else can test frameworks provide?


  • Driver management.
  • Advanced methods. Eg.
    self.assert_no_broken_links()
  • Test assertions. Eg.
    self.assert_text(TEXT, SELECTOR)
  • Command-line options. Eg.
    pytest --browser=edge --html=report.html
  • Advanced tools (Eg. test recorders)
  • Easy to read error messages. Eg.
    Element "h2" was not visible after 10s!

What about test runners?


Python includes powerful test runners, such as pytest.

What can pytest do?


  • Auto-collect tests to run.
  • Use markers for organizing tests.
  • Generate test reports.
  • Provide test assertions.
  • Multithread your tests.
  • Use a large number of existing plugins.

What about complete frameworks?


SeleniumBase combines the best of both Selenium and pytest into a super framework.

SeleniumBase features. (You already saw this slide!)


  • Driver management.
  • Advanced methods. Eg.
    self.assert_no_broken_links()
  • Test assertions. Eg.
    self.assert_text(TEXT, SELECTOR)
  • Command-line options. Eg.
    pytest --browser=edge --html=report.html
  • Advanced tools (Eg. test recorders)
  • Easy to read error messages. Eg.
    Element "h2" was not visible after 10s!

How do you get SeleniumBase?



pip install seleniumbase

SeleniumBase example test:


from seleniumbase import BaseCase
BaseCase.main(__name__, __file__)

class MyTestClass(BaseCase):
    def test_basics(self):
        self.open("https://www.saucedemo.com")
        self.type("#user-name", "standard_user")
        self.type("#password", "secret_sauce\n")
        self.assert_element("div.inventory_list")
        self.assert_exact_text("Products", "span.title")
        self.click('button[name*="backpack"]')
        self.click("#shopping_cart_container a")
        self.assert_exact_text("Your Cart", "span.title")
        self.assert_text("Backpack", "div.cart_item")
        self.click("button#checkout")
        self.type("#first-name", "SeleniumBase")
        self.type("#last-name", "Automation")
        self.type("#postal-code", "77123")
        self.click("input#continue")
        self.assert_text("Checkout: Overview")

Common SeleniumBase methods:


self.open(url)  # Navigate the browser window to the URL.
self.type(selector, text)  # Update field with the text.
self.click(selector)  # Click element with the selector.
self.click_link(link_text)  # Click link containing text.
self.check_if_unchecked(selector)  # Check checkbox.
self.uncheck_if_checked(selector)  # Uncheck checkbox.
self.select_option_by_text(dropdown_selector, option)
self.hover_and_click(hover_selector, click_selector)
self.drag_and_drop(drag_selector, drop_selector)
self.choose_file(selector, file_path)  # Upload a file.
self.switch_to_frame(frame)  # Switch into the iframe.
self.switch_to_default_content()  # Exit all iframes.
self.switch_to_parent_frame()  # Exit current iframe.
self.open_new_window()  # Open new window in same browser.
self.switch_to_window(window)  # Switch to browser window.
self.switch_to_default_window()  # Go to original window.
self.assert_element(selector)  # Verify element visible.
self.assert_text(text, selector)  # Substring assertion.
self.assert_exact_text(text, selector)  # String assert.

Common command-line options:


--browser=BROWSER  (Choose web browser. Default: "chrome".)
--edge / --firefox / --safari  (Browser Shortcut.)
--headless  (Run tests headlessly.  Default on Linux OS.)
--demo  (Slow down and see test actions as they occur.)
--slow  (Slow down the automation. Faster than Demo Mode.)
--reuse-session / --rs  (Reuse browser session for tests.)
--reuse-class-session / --rcs  (RS, but for class tests.)
--crumbs  (Clear cookies between tests reusing a session.)
--maximize  (Start tests with the web browser maximized.)
--dashboard  (Enable the SB Dashboard at dashboard.html)
--uc  (Enable undetected-chromedriver mode.)
--incognito  (Enable Incognito mode.)
--guest  (Enable Guest mode.)
-m=MARKER  (Run tests with the specified pytest marker.)
-n=NUM  (Multithread the tests using that many threads.)
-v  (Verbose mode. Print the full names of each test run.)
--html=report.html  (Create a detailed pytest-html report.)
--collect-only / --co  (Only show discovered tests. No run.)
--co -q  (Only show full names of discovered tests. No run.)
-x  (Stop running tests after the first failure is reached.)

Common console scripts:


sbase get [DRIVER] [OPTIONS]  # Eg. chromedriver
sbase methods  # List common Python methods
sbase options  # List common pytest options
sbase gui  # Open the SB GUI for pytest
sbase caseplans  # Open the SB Case Plans App
sbase mkdir [DIRECTORY]  # Create a test directory
sbase mkfile [FILE.py]  # Create a test file
sbase codegen [FILE.py] [OPTIONS]  # Record a test
sbase recorder  # Open the SB Recorder App
sbase mkpres  # Create a Presentation boilerplate
sbase mkchart  # Create a Chart boilerplate
sbase print [FILE]  # Print file to console
sbase translate [FILE.py] [OPTIONS]  # Translate
sbase extract-objects [SB_FILE.py]  # Get objects
sbase inject-objects [SB_FILE.py]  # Swap selectors
sbase objectify [SB_FILE.py]  # Get & swap objects
sbase revert-objects [SB_FILE.py]  # Undo objectify
sbase download server  # Get Selenium Grid JAR file
sbase grid-hub [start|stop] [OPTIONS]  # Start Grid
sbase grid-node [start|stop] --hub=[IP]  # Add Node

Live Demo Time!



(Starting with raw Selenium, and evolving that into SeleniumBase.)