QA Graphic
July 2, 2025

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.

Permalink
June 25, 2025

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.

Permalink
June 18, 2025

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.

Permalink
June 11, 2025

Pytest + 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.

Pytest Selenium Selector Cheat Sheet

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.

Permalink
June 4, 2025

Pytest 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.

Permalink
May 28, 2025

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 and csv 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.

Permalink
May 21, 2025

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.

Permalink
May 14, 2025

How 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.

Permalink
May 7, 2025

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:

Python
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.

Permalink
April 30, 2025

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

This method relies on the 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

MondayMedia Monday
TuesdayQA
WednesdayVeed
ThursdayBusiness
FridayMacintosh
SaturdayInternet Tools
SundayOpen Topic