
Adding Mobile Emulation to Your Pytest Test
Easily Add Mobile Testing to Your Test Suite
Learn how to simulate a mobile environment in your automated tests using Pytest and Chrome's mobile emulation features.
Why Use Mobile Emulation?
Testing how your website behaves on mobile devices is essential. Rather than using a physical device or a mobile browser emulator, Chrome allows you to simulate mobile conditions directly through WebDriver using mobileEmulation
settings.
Setting Up Mobile Emulation in Pytest
To demonstrate mobile emulation in Pytest, you'll need the following packages:
pytest
selenium
chromedriver
installed and in your system path
Here's a simple example that simulates a Nexus 5 device while accessing a webpage:
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
def test_uncloying_new():
domain = "http://www.cryan.com"
mobile_emulation = {
"deviceMetrics": {"width": 360, "height": 640, "pixelRatio": 3.0},
"userAgent": "Mozilla/5.0 (Linux; Android 4.2.1; en-us; Nexus 5 Build/JOP40D) "
"AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 "
"Mobile Safari/535.19",
"clientHints": {"platform": "Android", "mobile": True}
}
chrome_options = Options()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')
chrome_options.add_experimental_option("mobileEmulation", mobile_emulation)
driver = webdriver.Chrome(options=chrome_options)
driver.get(domain)
assert "Welcome" in driver.page_source
driver.quit()
What's Happening Here?
- deviceMetrics: Simulates screen dimensions and pixel density.
- userAgent: Overrides the default user agent to a mobile one.
- clientHints: Helps simulate how modern mobile browsers send platform data.
- Chrome Options: Sets up a headless browser with mobile emulation enabled.
Benefits of Mobile Emulation in Tests
- No need for actual devices or external tools.
- Fast, scriptable, and easy to integrate into CI/CD pipelines.
- Improves confidence that your site works across form factors.
Using --disable-gpu in Pytest with Selenium
Useful to prevent Crashes in Some Environment
When running Selenium tests in headless mode with Pytest, adding the --disable-gpu
flag to your Chrome options can improve stability and avoid rendering issues. This option is especially useful when executing tests on virtual machines or CI pipelines that lack dedicated GPU hardware.
Why Use --disable-gpu
?
- Prevents crashes in some environments when using headless mode
- Avoids rendering issues caused by GPU acceleration in headless Chrome
- Improves compatibility with CI servers like Jenkins, GitHub Actions, or GitLab
Basic Configuration Example
Here is how to configure the Chrome driver with both --headless
and --disable-gpu
flags:
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
def test_headless_chrome():
options = Options()
options.add_argument('--headless')
options.add_argument('--disable-gpu')
driver = webdriver.Chrome(options=options)
driver.get("https://example.com")
assert "Example Domain" in driver.title
driver.quit()
When to Use It
Use --disable-gpu
when running tests in:
- Docker containers
- Linux-based headless environments
- CI/CD systems that lack a GPU
Although modern Chrome versions may not strictly require --disable-gpu
when using --headless
, including it remains a reliable safeguard across various platforms and test configurations.
Input and Select Cheat Sheet
Handy Reference to add text when running Automation
Automating user input and form interactions is a core part of UI testing with Selenium. This cheat sheet provides quick reference examples for entering text and interacting with dropdown menus in a Pytest test suite.
Typing into Input Fields
from selenium.webdriver.common.by import By
# Find input by ID and enter text
username_input = driver.find_element(By.ID, "username")
username_input.clear()
username_input.send_keys("test_user")
# Find input by Name attribute
email_input = driver.find_element(By.NAME, "email")
email_input.send_keys("user@example.com")
# Input into a field using CSS selector
password_input = driver.find_element(By.CSS_SELECTOR, "input[type='password']")
password_input.send_keys("securePassword123")
Working with Select Menus
Selenium provides a helper class called Select
for interacting with dropdowns built using the HTML <select>
tag.
from selenium.webdriver.support.ui import Select
# Select option by visible text
dropdown = Select(driver.find_element(By.ID, "country"))
dropdown.select_by_visible_text("Canada")
# Select option by index
dropdown.select_by_index(2)
# Select option by value
dropdown.select_by_value("us")
Validating Input Values
# Assert input value is what we typed
assert username_input.get_attribute("value") == "test_user"
Bonus: Handling Auto-Suggest Inputs
# Type and wait for suggestion list, then select item
search_box = driver.find_element(By.ID, "search")
search_box.send_keys("Pytest")
# Wait and click suggestion
suggestion = driver.find_element(By.XPATH, "//li[contains(text(), 'Pytest Tutorial')]")
suggestion.click()
These examples serve as a solid foundation for common input tasks in your Selenium test cases. Keep this guide handy when building tests involving forms, login fields, or dropdown menus.
PermalinkPytest + Selenium Selector Type Cheat Sheet
Handy Guide to Have for find_elements
When using Pytest with Selenium, knowing which selector strategy to use can save time and boost test reliability. This cheat sheet summarizes the most common selector types along with syntax examples and practical use cases.
Below is a handy reference image you can download and keep for quick access during test writing.

Common Selector Examples
# ID
driver.find_element(By.ID, "login") # Fast and most reliable
# Name
driver.find_element(By.NAME, "username") # Often used in forms
# Class Name
driver.find_element(By.CLASS_NAME, "btn-primary") # Good for unique CSS classes
# Tag Name
driver.find_element(By.TAG_NAME, "input") # Generic, works when no better option exists
# CSS Selector
driver.find_element(By.CSS_SELECTOR, ".menu > li") # Flexible, powerful, great for nested elements
# XPath
driver.find_element(By.XPATH, "//div[@id='main']") # Very powerful for complex DOMs
# Link Text
driver.find_element(By.LINK_TEXT, "Sign in") # When matching exact visible link text
# Partial Link Text
driver.find_element(By.PARTIAL_LINK_TEXT, "Sign") # For dynamic or lengthy link text
Understanding the strengths of each selector type can help you write more stable and maintainable Selenium tests. Keep this cheat sheet nearby during your next automation sprint.
PermalinkPytest and Selenium: Finding All Anchor Elements
Useful Tips and Trick with Elements
When performing web automation tests, a common task is to interact with or verify links on a webpage. Using Pytest with Selenium makes this process straightforward. This post will walk you through a simple way to find all anchor elements (<a>
tags) on a page.
The Basics of Finding Elements
Selenium provides various methods to locate elements on a webpage. One of the most fundamental is find_elements
, which allows you to retrieve a list of all elements matching a specified locator strategy. To find anchor tags, we use By.TAG_NAME
.
Sample Code for Pytest
Here's a simple Pytest example demonstrating how to find all link elements on a page. We assume you have a Selenium WebDriver instance (driver
) already set up.
from selenium import webdriver
from selenium.webdriver.common.by import By
import pytest
def test_find_all_links(driver):
# Navigate to a sample page (replace with your URL)
driver.get("https://www.example.com")
# Find all link elements
links = driver.find_elements(By.TAG_NAME, "a")
# You can then iterate through the links or perform assertions
print(f"Found {len(links)} anchor elements.")
for link in links:
if link.text: # Print link text if available
print(f"Link Text: {link.text}, URL: {link.get_attribute('href')}")
else: # If no text, just print the URL
print(f"URL: {link.get_attribute('href')}")
# Example assertion: check if at least one link exists
assert len(links) > 0, "No anchor elements found on the page."
Explanation
-
driver.get("https://www.example.com")
: This line navigates your WebDriver instance to the specified URL. Remember to replace "https://www.example.com" with the actual URL of the page you are testing. -
links = driver.find_elements(By.TAG_NAME, "a")
: This is the core of the operation.-
driver.find_elements
: This method returns a list of web elements. If no elements are found, it returns an empty list. -
By.TAG_NAME
: This is the locator strategy, indicating that we want to find elements by their HTML tag name. -
"a"
: This is the value for our locator strategy, specifically targeting the anchor tag.
-
- The subsequent loop iterates through the `links` list. For each `link` element, we print its text (if available) and its `href` attribute, which contains the URL.
-
assert len(links) > 0
: A simple assertion to ensure that at least one anchor element was found. This is a basic example; in real tests, you would have more specific assertions based on your requirements.
Recording Page Load Time with Pytest and Saving to CSV
Automatically Track the Page Load Time for Performance checking
Tracking page load performance is a critical part of maintaining a fast and user-friendly website. In this tutorial, we will use pytest
and Python’s built-in tools to measure how long a page takes to load and save the results to a CSV file. Each row in the CSV will include a timestamp and the corresponding load time.
What You Need
pytest
requests
datetime
andcsv
from the standard library
The Code
This code fetches a web page, measures the response time, and logs the result with a timestamp to a CSV file.
import pytest
import requests
import time
import csv
from datetime import datetime
URL_TO_TEST = "https://www.americanfreight.com/"
CSV_FILE = "page_load_times.csv"
def write_to_csv(timestamp, load_time):
"""Append timestamp and load time to the CSV file."""
with open(CSV_FILE, mode="a", newline="") as file:
writer = csv.writer(file)
writer.writerow([timestamp, load_time])
@pytest.mark.parametrize("url", [URL_TO_TEST])
def test_page_load_time(url):
"""Measure the page load time and write to a CSV file."""
start_time = time.time()
response = requests.get(url)
end_time = time.time()
load_time = round(end_time - start_time, 3)
timestamp = datetime.now().isoformat(timespec="seconds")
assert response.status_code == 200, f"Failed to load page: {response.status_code}"
write_to_csv(timestamp, load_time)
print(f"Page loaded in {load_time} seconds at {timestamp}")
Sample CSV Output
Here is what the CSV file will look like after a few test runs:
2025-05-21T13:00:01,1.276
2025-05-21T13:10:43,1.304
2025-05-21T13:20:22,1.142
Automating in CI
You can easily integrate this into your CI pipeline to monitor page load performance over time. Combine it with cron jobs or GitHub Actions to schedule regular runs and keep tabs on performance degradation.
Tips
- Use
requests.Session()
for persistent connections if testing multiple pages. - Extend the CSV format to include response size or headers if needed.
- Visualize the results using tools like Excel or Python’s
matplotlib
.
Conclusion
Measuring and recording page load times with pytest
is simple yet powerful. With just a few lines of code, you can start building a performance history and catching slowdowns before they impact your users.
Validating Sitemap Namespace with Pytest
Basic Pytest setup
Sitemaps help search engines index your website correctly. But a malformed or incorrectly-namespaced sitemap can cause issues with indexing. This post walks through using pytest
to validate that your sitemap uses the correct XML namespace.
Why Namespace Validation Matters
The XML namespace ensures the sitemap conforms to the sitemaps.org protocol. If the namespace is incorrect or missing, search engines may ignore your sitemap, leading to poor visibility.
The Test Code
Below is the code that checks whether the sitemap at https://www.americanfreight.com/sitemap.xml
is valid and correctly namespaced:
import pytest
import requests
import xml.etree.ElementTree as ET
from urllib.parse import urlparse
# Define the sitemap URL
SITEMAP_URL = "https://www.americanfreight.com/sitemap.xml"
@pytest.fixture
def sitemap_content():
"""Fixture to fetch and return the sitemap XML content."""
response = requests.get(SITEMAP_URL)
assert response.status_code == 200, f"Failed to fetch sitemap: {response.status_code}"
return response.content
@pytest.fixture
def sitemap_root(sitemap_content):
"""Fixture to parse the sitemap XML and return the root element."""
return ET.fromstring(sitemap_content)
def test_sitemap_fetch(sitemap_content):
"""Test that the sitemap can be fetched successfully."""
assert sitemap_content is not None, "Sitemap content is empty"
assert b"<?xml" in sitemap_content, "Sitemap is not valid XML"
def test_sitemap_root_namespace(sitemap_root):
"""Test that the sitemap has the correct XML namespace."""
expected_namespace = "http://www.sitemaps.org/schemas/sitemap/0.9"
assert sitemap_root.tag == f"{{{expected_namespace}}}urlset",
f"Unexpected root tag or namespace: {sitemap_root.tag}"
Running the Test
To run the tests, use the following command in your terminal:
pytest test_sitemap_namespace.py
Test Breakdown
- sitemap_content: Fetches the sitemap and ensures it's accessible (status 200).
- sitemap_root: Parses the XML content for further inspection.
- test_sitemap_fetch: Confirms the sitemap starts with the
<?xml
declaration. - test_sitemap_root_namespace: Verifies the root element includes the correct namespace defined by sitemaps.org.
Conclusion
These simple but powerful tests can catch issues early in your CI pipeline. Whether you're managing SEO for a large e-commerce platform or a personal blog, namespace correctness should be a non-negotiable in your automated checks.
PermalinkHow to Check URL Validity
Quick and Easy
A quick guide to validating URLs after a release using Pytest and Python's requests library.
Introduction
After a software release, ensuring that all URLs are accessible is critical. This blog post demonstrates how to use Pytest and the requests library to check if URLs return a 200 status code, indicating they are valid and accessible. This approach is fast, reliable, and easy to integrate into your testing pipeline.
Prerequisites
- Python 3.6 or higher
- Pytest (
pip install pytest
) - Requests library (
pip install requests
)
Sample Code
Below is a simple Pytest script to check if a URL returns a 200 status code:
import pytest
import requests
def test_url_returns_200():
url = "https://www.cryan.com"
try:
response = requests.get(url, timeout=5)
assert response.status_code == 200
except requests.RequestException as e:
pytest.fail(f"Request failed: {e}")
This test sends a GET request to the specified URL and checks if the response status code is 200. If the request fails (e.g., due to a timeout or network issue), the test fails with an error message.
Running the Test
Save the code in a file (e.g., test_urls.py
) and run it using the following command:
pytest test_urls.py -v
The -v
flag provides verbose output, showing the test results in detail.
Scaling to Multiple URLs
To test multiple URLs, you can use Pytest's parameterization feature. Here's an example:
import pytest
import requests
@pytest.mark.parametrize("url", [
"https://www.cryan.com",
"https://www.example.com",
"https://www.python.org",
])
def test_url_returns_200(url):
try:
response = requests.get(url, timeout=5)
assert response.status_code == 200
except requests.RequestException as e:
pytest.fail(f"Request failed for {url}: {e}")
This script tests multiple URLs in a single test function, making it efficient for checking several endpoints after a release.
Best Practices
- Set a reasonable timeout (e.g., 5 seconds) to avoid hanging tests.
- Use parameterization to test multiple URLs efficiently.
- Integrate tests into your CI/CD pipeline for automated checks post-release.
- Log failures with detailed messages to aid debugging.
Conclusion
Using Pytest and the requests library, you can quickly validate URLs after a release. This approach is simple, scalable, and integrates well with automated testing workflows. By incorporating these tests into your pipeline, you can ensure your application's URLs remain accessible and reliable.
In Pytest, what is the Best Date/Time Format for Filenames
Basic Filename Format to find Files
The best date/time format for filenames is ISO 8601 with modifications to ensure compatibility and readability. I recommend using YYYYMMDD_HHMMSS
(e.g., 20250430_143022
) for the following reasons:
- Sortability: ISO 8601 (year-month-day) ensures files sort chronologically when listed.
- Uniqueness: Including seconds prevents filename collisions during rapid test runs.
- Readability: The format is clear and universally understood.
- Filesystem Safety: Replacing colons (
:
) with underscores (_
) avoids issues on filesystems that don't allow colons in filenames.
Here's an example of generating a timestamped filename in pytest
:
import pytest
from datetime import datetime
def get_timestamped_filename(base_name, extension):
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
return f"{base_name}_{timestamp}.{extension}"
# Example usage in a test
def test_example():
filename = get_timestamped_filename("screenshot", "png")
# Save screenshot with filename like "screenshot_20250430_143022.png"
print(f"Saving screenshot as {filename}")
Tip: If you need microseconds for high-frequency tests, use datetime.now().strftime("%Y%m%d_%H%M%S_%f")
to include microseconds (e.g., 20250430_143022_123456
).
Alternative: For human-readable logs, you might include a readable date in the file content but keep the filename simple and sortable. For example, save a log with a header like Test Run: April 30, 2025 14:30:22
inside the file, but name the file log_20250430_143022.txt
.
Check Image Sizes on Production Websites
Using Python and Pytest to Catch Bloated Images Before They Slow Down Your Site
Large images can slow down page performance and negatively impact user experience. During one of my past QA roles, I created a simple Python function to detect when an image might be too large based on its content length header.
Use Case
Our QA team was tasked with verifying the size of images on the production site to ensure they met
optimization standards. While tools like Lighthouse can highlight these issues, I needed something
scriptable - and Pytest
+ requests
was a lightweight and perfect solution.
Sample Code
Here’s a simplified version of the utility function I used in our automated tests:
import requests
def check_image_size(img_url, base_url="https://www.cryan.com"):
# Construct full URL if necessary
if not img_url.startswith(('http://', 'https://')):
img_url = base_url + img_url if img_url.startswith('/') else base_url + '/' + img_url
try:
response = requests.get(img_url, stream=True)
response.raise_for_status()
# Check content length if provided in headers
content_length = response.headers.get('Content-Length')
if content_length and int(content_length) > 1000:
return f"Image at {img_url} might be too large: Header indicates {int(content_length)/1000}KB"
except Exception as e:
return f"Error checking image size: {e}"
return f"Image at {img_url} appears to be an acceptable size."
How to Use It in Pytest
Integrate this into your test suite by looping through known image paths:
def test_image_sizes():
image_paths = [
"/images/logo.png",
"/media/banner.jpg",
"/assets/hero-large.jpg"
]
for img in image_paths:
result = check_image_size(img)
assert "too large" not in result, result
Pro Tip
Content-Length
header, which isn't always accurate for dynamically
generated or CDN-compressed images. For full accuracy, you could download the stream and measure bytes.
Dependencies
This example uses the requests
library:
pip install requests
Permalink
About
Welcome to Pytest Tips and Tricks, your go-to resource for mastering the art of testing with Pytest! Whether you're a seasoned developer or just dipping your toes into the world of Python testing, this blog is designed to help you unlock the full potential of Pytest - one of the most powerful and flexible testing frameworks out there. Here, I'll share a treasure trove of practical insights, clever techniques, and time-saving shortcuts that I've gathered from years of writing tests and debugging code.
Check out all the blog posts.
Blog Schedule
Monday | Media Monday |
Tuesday | QA |
Wednesday | Veed |
Thursday | Business |
Friday | Macintosh |
Saturday | Internet Tools |
Sunday | Open Topic |
Other Posts
- Validating Sitemap Namespace with Pytest
- mocker.spy
- Pytest + Selenium Selector Type Cheat Sheet
- Capturing Screenshots in Fixture Teardown
- Spell-Check Your Site Using Pytest
- Using pytest.raises to Validate Exceptions Like a Pro
- Using --disable-gpu in Pytest with Selenium
- PyTest Install
- Verifying Your Pytest Setup with a Simple Selenium Test
- Parametrization in Pytest
- Recording Page Load Time with Pytest and Saving to CSV
- In Pytest, what is the Best Date/Time Format for Filenames
- How to Check URL Validity
- Pytest and Selenium: Finding All Anchor Elements
- Input and Select Cheat Sheet