Selenium Testing Library (STL) is a Python library implementing Testing-Library in Selenium.
- Python 3.8, 3.9, 3.10, 3.11, 3.12, 3.13-dev
selenium
>= 3.0.0
pip install selenium-testing-library
from selenium import webdriver
from selenium_testing_library import Screen
driver = webdriver.Chrome()
driver.open('https://google.com/')
screen = Screen(driver)
search_input = screen.find_by_title("Search")
search.send_keys("Dogs")
search_button = screen.find_by_text("Google Search")
search_button.click()
screen.wait_for_stale(search_button)
STL implements the Queries API from the Testing Library. The Testing Library queries get_by
, query_by
, find_by
, and the multiple element equivalents get_all_by
, query_all_by
, find_all_by
are used in places where you would normally use Selenium's find_element
and find_elements
functions.
The difference between the different queries (get_by
, query_by
, find_by
) is whether the query will throw an error if the element was not found (get_by
), return None
(query_by
) or block, wait and retry until the element is found (find_by
).
get_by
returns the element matched and throws an exception if zero or more than one element matches. This is the main function that we should be using to locate elements on a page.query_by
returns the element matched orNone
if no element match. It throws an exception if more than one element matches. Mostly used for asserting that an element is not present:assert not screen.query_by_text("not on page")
.find_by
behaves likeget_by
, but uses aWebDriverWait
to wait until the element is present in the DOM.get_all_by
returns a list of elements matched. It raises an exception if no elements match.query_all_by
returns a list of elements matched. It returns an empty list when no elements match.find_all_by
behaves likeget_all_by
, but uses aWebDriverWait
to wait until the elements are present in the DOM.
When an element is found the queries return a Selenium WebElement
or a list containing Selenium WebElements when using get_all_by
, query_all_by
, find_all_by
.
The queries accept a tuple containing the By class identifier and the search query, so they can be used with XPath, Css or any other native Selenium selector:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium_testing_library import Screen
screen = Screen(webdriver.Chrome())
screen.get_by((By.CSS, ".my_class")) # Will throw an exception if the element is not found
screen.query_by((By.ID, "my_id")) # you can use regular tuples as if you were using Selenium's find_element()
screen.find_by((By.XPATH, "//div"), timeout=5, poll_frequency=0.5) # locators for searching through text also work
For convenience Locator classes can be used instead of the tuples:
from selenium import webdriver
from selenium_testing_library import Screen, locators
screen = Screen(webdriver.Chrome())
screen.get_by(locators.Css(".my_class")) # Will throw an exception if the element is not found
screen.query_by(locators.Id("my_id")) # you can use regular tuples as if you were using Selenium's find_element()
screen.find_by(locators.XPath("//div"), timeout=5, poll_frequency=0.5) # locators for searching through text also work
Besides all the Selenium native By selectors, the queries also support Testing Library's selectors:
from selenium import webdriver
from selenium_testing_library import Screen, locators
screen = Screen(webdriver.Chrome())
screen.get_by(locators.Text("My Text"))
screen.query_by(locators.Role("button", pressed=True))
screen.find_by(locators.TestId("my-test"), timeout=5, poll_frequency=0.5) # locators for searching through text also work
For convenience helper functions on the screen class are available to avoid instantiating locator classes all over the place:
screen.get_by_role(role_name)
Queries for elements with the given role.
screen.get_by_label_text(text)
Queries for label elements that match the text string and return the corresponding input element.
screen.get_by_placeholder_text(text)
Queries elements with the matching placeholder attribute.
screen.get_by_text(text)
Queries elements where the content matches the provided text.
screen.get_by_display_value(value)
Queries inputs, textareas, or selects with matching display value.
screen.get_by_alt_text(text)
Queries elements with the matching alt attribute.
screen.get_by_title(text)
Queries elements with the matching title attribute.
screen.get_by_test_id(value)
Queries elements matching the data-testid
value.
screen.get_by_css(css)
Queries elements matching the specified css selector.
screen.get_by_xpath(xpath)
Queries elements matching the specified xpath selector.
There are also query_by_*
, find_by_*
, get_all_by_*
, query_all_by_*
, find_all_by_*
equivalents.
Note: The selenium project has removed the find_element_by_*
and find_elements_by_*
helper functions in the Selenium 4.3.0 release, so I just want to state that the screen
helper functions will never be deprecated or removed.
Examples:
from selenium import webdriver
from selenium_testing_library import Screen
screen = Screen(webdriver.Chrome())
screen.query_by_role("role_name")
screen.get_by_label_text("label text")
screen.find_all_by_text("my text", timeout=5, poll_frequency=0.5)
screen.get_all_by_alt_text("alt text")
wait_for(condition_function)
Waits until the condition function returns a truthy value.
wait_for_stale(element)
Waits until the element is removed from the DOM.
Examples:
from selenium import webdriver
from selenium_testing_library import Screen, locators
screen = Screen(webdriver.Chrome())
# Wait for the element to be clickable:
element = screen.get_by_text("Submit")
screen.wait_for(lambda _: element.is_enabled(), timeout=5, poll_frequency=0.5)
# Wait for the element to be removed from the page:
screen.wait_for_stale(element)
Within(element)
Used to limit the query to the children of the provided element
Example:
from selenium import webdriver
from selenium_testing_library import Screen, Within
screen = Screen(webdriver.Chrome())
parent_element = screen.get_by_css(".container")
Within(parent_element).get_by_title("My title inside the container")
For debugging using testing-playground, screen
exposes log_testing_playground_url()
which prints end returns a URL that can be opened in the browser.
# log entire document to testing-playground
url = screen.log_testing_playground_url()
# log a single element
url = screen.log_testing_playground_url(screen.get_by_text("test"))
Setting up a local development environment
git clone https://github.com/anze3db/selenium-testing-library.git && cd selenium-testing-library
poetry install && poetry shell
# Make sure `chromedriver` is in your PATH, download from https://chromedriver.chromium.org/downloads
# run tests:
pytest --selenium-headless
# run tests and display coverage info:
pytest --selenium-headless --cov=selenium_testing_library --cov-report html
# To test on multiple Python versions make sure that py37, py38, py39 are
# installed on your system and available through python3.7, python3.8,
# python3.9. (Use pyenv and add the pyenv shims to your path
# `export PATH=$(pyenv root)/shims:$PATH`). Then run tox:
tox
- Update Changelog
- Update npm dependencies
npm run deploy
If npm run deploy
fails because of outdated dependencies run:
npm update
- Bump the version
bumpver update # Wait and see if the CI is green
- Publish to PyPI
poetry build && poetry publish