QA Graphic
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
April 23, 2025

Verifying Your Pytest Setup with a Simple Selenium Test

Quick Test to make sure proper installation

In this post, we'll walk through creating a basic Pytest test that uses Selenium WebDriver to navigate to www.google.com and validate that the word "Google" appears in the page title. This test serves as a lightweight check to ensure your Pytest environment is ready to go.

Prerequisites

Before we dive in, make sure you have the following installed:

  • Python: Version 3.7 or higher.
  • Pytest: Install it using pip install pytest or python3 -m pip install pytest --user.
  • Selenium WebDriver: Install it using pip install selenium or python3 -m pip install selenium --user.
  • Web Browser Driver: For this example, we'll use ChromeDriver. Download it from the ChromeDriver website and ensure it matches your Chrome browser version. Place the chromedriver executable in your system’s PATH or specify its location in the code.

Setting Up the Project

  1. Create a new directory for your project, e.g., pytest_setup_test.
  2. Inside the directory, create a virtual environment (optional but recommended):
    python -m venv venv
    source venv/bin/activate  # On Windows: venvScriptsactivate
  3. Install the required packages:
    pip install pytest selenium
  4. Create a file named test_google.py to hold our test.

Writing the Pytest Test

Here’s the code for test_google.py, which uses Selenium to navigate to www.google.com and checks the page title:

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
import pytest
# Fixture to set up and tear down the WebDriver
@pytest.fixture
def browser():
    # Specify the path to ChromeDriver if not in PATH
    # service = Service('/path/to/chromedriver')  # Uncomment and update if needed
    service = Service()
    driver = webdriver.Chrome(service=service)
    yield driver
    driver.quit()
# Test to validate the page title of google.com
def test_google_title(browser):
    browser.get("https://www.google.com")
    title = browser.title
    assert "Google" in title, f"Expected 'Google' in title, but got '{title}'"

Explanation

  • Fixture (browser): The pytest.fixture decorator defines a reusable setup/teardown function. Here, it initializes a Chrome WebDriver instance and yields it to the test. After the test runs, driver.quit() closes the browser.
  • Test Function (test_google_title): This function uses the browser fixture to:
    1. Navigate to https://www.google.com using browser.get().
    2. Retrieve the page title with browser.title.
    3. Assert that the word "Google" is in the title. If not, the test fails with a descriptive message.
  • ChromeDriver Path: If ChromeDriver is not in your system’s PATH, uncomment the service line and specify the path to the chromedriver executable.

Running the Test

To run the test, navigate to your project directory in the terminal and execute:

pytest test_google.py -v

Expected Output

If everything is set up correctly, you should see output similar to this:

============================= test session starts ==============================
platform linux -- Python 3.9.5, pytest-7.4.3, pluggy-1.0.0
collected 1 item
test_google.py::test_google_title PASSED                                  [100%]
============================== 1 passed in 2.34s ===============================
  • PASSED: Indicates the test ran successfully, confirming that Pytest and Selenium are configured correctly, and the word "Google" was found in the page title.
  • FAILED: If the test fails, check the error message. Common issues include:
    • ChromeDriver not found (verify the path or PATH configuration).
    • Network issues preventing access to www.google.com.
    • Mismatched ChromeDriver and Chrome browser versions.

Troubleshooting Tips

  • ChromeDriver Issues: Ensure ChromeDriver matches your Chrome version (check Chrome’s version in Settings > About Chrome). Update or reinstall ChromeDriver if needed.
  • Pytest Not Found: Verify Pytest is installed in your active environment (pip show pytest).
  • Selenium Errors: Confirm Selenium is installed (pip show selenium) and that you’re using a compatible version with your browser.
  • ﹤li class="list-group-item">Test Fails Due to Title: Occasionally, Google’s title might vary (e.g., due to localization). You can modify the assertion to be more flexible if needed.

Why This Test?

This test is a simple yet effective way to validate your Pytest setup because it:

  • Confirms Pytest is installed and running tests correctly.
  • Verifies Selenium WebDriver is configured and can interact with a browser.
  • Checks network connectivity and browser compatibility.
  • Provides a clear pass/fail outcome with minimal code.

Next Steps

Once this test passes, your Pytest environment is ready! You can expand your test suite to include more complex scenarios, such as:

  • Testing form submissions or button clicks.
  • Validating other websites or web applications.
  • Integrating with CI/CD pipelines for automated testing.

Feel free to tweak this test to suit your needs. For example, you could test a different website or validate other page elements like text or links. Happy testing!

Permalink
April 16, 2025

Spell-Check Your Site Using Pytest

Use Automation to Check Spelling

Here's a clever way to catch embarrassing spelling mistakes on your website using pytest and spellchecker. This script can be scheduled to run periodically to ensure nothing slips through the cracks!

Why Check for Spelling?

Spelling errors can reduce trust, affect SEO, and just look unprofessional. With this script, you can automatically scan your site's content and get alerted if anything seems off.

Dependencies

  • pytest
  • requests
  • beautifulsoup4
  • pyspellchecker

Install them with:

pip install pytest requests beautifulsoup4 pyspellchecker

The Test Code

Here is the full test you can drop into your test suite:

import pytest
import requests
from bs4 import BeautifulSoup
from spellchecker import SpellChecker
from urllib.parse import urljoin
def get_visible_text(url):
    try:
        response = requests.get(url, timeout=10)
        response.raise_for_status()
    except requests.RequestException as e:
        pytest.fail(f"Failed to fetch {url}: {e}")
    soup = BeautifulSoup(response.text, 'html.parser')
    for element in soup(['script', 'style', 'header', 'footer', 'nav', 'aside']):
        element.decompose()
    text = soup.get_text(separator=' ', strip=True)
    return text
def check_spelling(text, custom_words=None):
    spell = SpellChecker()
    if custom_words:
        spell.word_frequency.load_words(custom_words)
    words = text.split()
    misspelled = spell.unknown(words)
    return misspelled
def test_spelling_cryan_com():
    url = "https://www.cryan.com"
    custom_words = ["cryan", "blog", "tech", "xai"]
    text = get_visible_text(url)
    misspelled_words = check_spelling(text, custom_words=custom_words)
    assert not misspelled_words, (
        f"Spelling errors found on {url}: {misspelled_words}"
    )
if __name__ == "__main__":
    pytest.main(["-v", __file__])

Customization Tips

  • Add more custom words to avoid false positives like brand names or domain-specific terms.
  • Expand to multiple pages by looping through URLs and running the same logic.
  • Integrate with CI/CD for automatic detection during deployment.

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

Wednesday 18 Pytest
Thursday 19 PlayWright
Friday 20 Macintosh
Saturday 21 Internet Tools
Sunday 22 Misc
Monday 23 Media
Tuesday 24 QA