惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

F
Full Disclosure
WordPress大学
WordPress大学
小众软件
小众软件
Cloudbric
Cloudbric
AWS News Blog
AWS News Blog
腾讯CDC
量子位
人人都是产品经理
人人都是产品经理
大猫的无限游戏
大猫的无限游戏
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
V
Vulnerabilities – Threatpost
Scott Helme
Scott Helme
Hugging Face - Blog
Hugging Face - Blog
博客园_首页
C
CXSECURITY Database RSS Feed - CXSecurity.com
The Hacker News
The Hacker News
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
IT之家
IT之家
Jina AI
Jina AI
Attack and Defense Labs
Attack and Defense Labs
S
SegmentFault 最新的问题
Simon Willison's Weblog
Simon Willison's Weblog
The Cloudflare Blog
阮一峰的网络日志
阮一峰的网络日志
T
Tailwind CSS Blog
Last Week in AI
Last Week in AI
博客园 - 【当耐特】
Google Online Security Blog
Google Online Security Blog
美团技术团队
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
V
Visual Studio Blog
罗磊的独立博客
L
LINUX DO - 最新话题
博客园 - Franky
博客园 - 叶小钗
Apple Machine Learning Research
Apple Machine Learning Research
The Last Watchdog
The Last Watchdog
J
Java Code Geeks
AI
AI
C
Cisco Blogs
酷 壳 – CoolShell
酷 壳 – CoolShell
C
Cyber Attacks, Cyber Crime and Cyber Security
Cisco Talos Blog
Cisco Talos Blog
博客园 - 三生石上(FineUI控件)
雷峰网
雷峰网
Help Net Security
Help Net Security
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
云风的 BLOG
云风的 BLOG
I
Intezer
S
Securelist

Anže's Blog

The 15-Year-Old iptables Rule That Broke My DNS Fedidevs 9h Outage Postmortem Letting Claude Upgrade My Raspberry Pi Agents Day Lisbon DjangoCon Europe 2026 How to Safely Update Your Dependencies Speeding Up Django Startup Times with Lazy Imports Typing Your Django Project in 2026 Claude Fixes User Bug Jekyll to Hugo Migration Advent of Code 2025 🎄 Django bulk_update Memory Issue Migrating Gunicorn to Granian Disable Network Requests When Running Pytest Disable Runserver Warning in Django 5.2 Autogenerating og:images with Jekyll Power Outages and Gunicorn PID Files UV with Django Go-like Error Handling Makes No Sense in JavaScript or Python Packages Do Not Match the Hashes Pip Error Gotchas with SQLite in Production Fedidevs Dev Update #2 Django SQLite Production Config Django Streaming HTTP Responses Deploying a Django Project to My Raspberry Pi (Video) Thoughts on Code Reviews Django SQLite Benchmark Django, SQLite, and the Database Is Locked Error No Downtime Deployments with Gunicorn SQLite Write-Ahead Logging Writing a Pytest Plugin Fedidevs Dev Update #1 Django-TUI: A Text User Interface for Django Commands Automate Hatch Publish with GitHub Actions Words TUI: App for Daily Writing Textual App Auto Reload RDS Blue/Green Deployments Fly.io Certificate Renewal The Fastest Way to Build a Read-only JSON API import __hello__ Enum with `str` or `int` Mixin Breaking Change in Python 3.11 Your Code Doesn't Have to Be Perfect Fixing _SixMetaPathImporter.find_spec() Not Found Warnings in Python 3.10 Upgrading Django App to Python 3.10 Integer Overflow Error in a Python Application Python Dependency Management MySQL Performance Degradation in Django 3.1 New Features in Python 3.8 and 3.9 The Code Review Batch Size The Code Review Bottleneck
Using Testing Library with Selenium in Python
Anže Pečar · 2023-01-30 · via Anže's Blog

The Testing Library has become one of the most popular testing tools for writing tests and it currently supports numerous front-end frameworks (React, Vue, Angular, Cypress, and more). The Testing Library query API has even been adopted by Playwright in their recent release.

The popularity of the Testing Library probably boils down to the following two reasons:

  1. Well-designed query API
  2. Focus on accessibility

Query API

On first look, the Testing Library Query API looks complex. There are 3 types of queries:

  1. getBy... returns the matching node and throws an error if nothing is matched.
  2. queryBy... works the same as getBy... but returns null instead of an error if nothing matched.
  3. findBy... returns a promise that is rejected if the element was not found in 1s (or based on the timeout parameter).

You mainly use the get queries since they are the fastest. You use query when you need to assert that something shouldn’t be in the DOM. You use find to find elements that might not be in the DOM just yet.

Accessibility

The main guiding principle of the Testing Library is that tests should be written to closely resemble how the web pages are going to be used.

This shows itself the most in the priority for using different locators. ...ByRole should be your top preference since it can be used to query every element exposed in the accessibility tree. On the other hand, getByTestId should be used as the last resort because end users have no way of seeing or hearing it.

Selenium with Python

In Selenium with Python we, unfortunately, don’t get a good Query API or accessibility features out of the box.

Query API

The Query API consists of two functions to query elements. find_element and find_elements. This seems simpler, but it makes test code much more verbose.

First example:

# Assert that only one element is found in the DOM
from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Firefox()
assert len(driver.find_elements(By.Name, "my-element")) == 1

We can’t use find_elment for this because it doesn’t catch the case when there is more than one element with the name my-element on the page. In that case, the function returns the first element found and ignores all the other instances.

Second example:

# Wait until the element is visible on the page
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver = webdriver.Firefox()
element = WebDriverWait(driver, 1).until(
    EC.presence_of_element_located((By.Name, "my-element"))
)

Waiting for the element to appear on the page is even more verbose and it requires four different import statements. I see teams create their own wait abstractions to get around this which isn’t ideal. You could use implicit waits instead, but they usually aren’t recommended.

Accessibility

Some recently added Selenium features have focused on accessibility (relative Locators added in 4.0) but, in general, the preferred locators for finding elements in Selenium continue to be CSS or XPath expressions. Users generally don’t see your CSS classes or XPath expressions so they should not be your default choice. Even worse, developers like to change those, and that makes your end-to-end tests brittle.

Selenium also doesn’t offer the primary locator recommended by the Testing Library - By Role. This is regrettable since it’s extremely difficult to write your own. I’ve tried, but implementing this table of HTML elements and their roles goes beyond a weekend project 😓

The Solution

The good news is that it’s fairly easy to use the Testing Library from Selenium. Since the dom-testing-library is written in JavaScript you can add it to your webpage and then use its API within your tests. Unfortunately, this looks a unplesant in Python.

First example rewritten using this approach:

# Assert that only one element is found on the page
from selenium import webdriver

driver = webdriver.Firefox()
driver.execute_script("getByLabelText(document, 'My Element") # getBy query raises an exception if more than one element is found

Second example:

# Wait until the element is visible on the page
from selenium import webdriver

driver = webdriver.Firefox()
driver.execute_script("findByLabelText(document, 'My Element") # findBy query will wait for 1s before throwing an error

I’d argue that using execute_script is already an improvement over Selenium’s API. We can even locate the input element using its label text, something that is not easily done with Selenium.

Selenium Testing Library

However, injecting the Testing Library into the DOM manually and then writing JavaScript is not the best user experience for writing tests. This is why I created a PyPI package Selenium Testing Library that takes care of this plumbing and exposes a nice 100% type annotated API.

First example rewritten using the Selenium Testing Library:

# Assert that only one element is found on the page
from selenium import webdriver
from selenium_testing_library import Screen

screen = Screen(webdriver.Firefox())
screen.get_by_label_text("My Element")

Second example:

# Wait until the element is visible on the page
from selenium import webdriver
from selenium_testing_library import Screen

screen = Screen(webdriver.Firefox())
screen.find_by_label_text("My Element")

The Screen class exposes all the get_by..., query_by... and find_by... methods as well as all the Testing Library locators (Role, Label Test, TestId, and others). I even added the Selenium locators including XPath, CSS, and others so that it’s easier to transition.

The Selenium Testing Library has been used by my company for more than one year now and it powers more than 600 end-to-end tests. The API has been stable and I am not planning on making any backward incompatible changes. Hopefully, DOM Testing Library doesn’t make them either 🤞

Fin

I think the Testing Library has come up with a really great way for writing front-end tests and I hope to see Selenium adopt some of these ideas in the future so that 3rd party packages like Selenium Testing Library will no longer be necessary.