Learn Selenium - Assertions & Test Validation
Episode 7 of 23

Learn Selenium - Assertions & Test Validation

This episode covers assertions with pytest to validate page state, text, element visibility, and behavior, composing dynamic assertions for responsive UI, and adding error reporting and automatic screenshots when tests fail.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

Finding elements and interacting with them is only half the story. The other half is validation: proving the results meet expectations. Episode 7 covers assertions and test validation — how to validate page state, text, element visibility, and application behavior, and how to make test failures easy to trace through error reporting and screenshots.

Assertions are the heart of the difference between "automation is running" and "tests that truly validate". A script that only clicks without checking results provides no value at all. In this episode you'll learn to choose the right assertion and place it at the right moment.

Assertions with pytest

The assert Syntax

Python uses the assert statement with standard comparison operators. pytest catches assert failures and shows the actual vs expected values in detail:

PythonBasic assertion examples
judul = driver.title
assert judul == "Dashboard"
 
jumlah_item = len(driver.find_elements("css selector", ".cart-item"))
assert jumlah_item > 0
assert jumlah_item == 3, f"Jumlah item {jumlah_item}, diharapkan 3"

assert jumlah_item == 3, "pesan" adds a message that appears on failure. This message is very helpful when tracing failures in CI, so make a habit of always writing one.

Choosing the Right Assertion

The rule of thumb: assert what really matters to the business. Don't assert trivial details that only make tests brittle, like CSS attribute ordering. Assert functional outcomes: text appears, URL changes, element count matches, or a specific state is active.

Validating Page State

Text, Visibility, and Attributes

The classic combination for validating a page:

PythonValidating text and visibility
selamat = driver.find_element("id", "welcome")
assert selamat.is_displayed()
assert "Selamat datang" in selamat.text
 
tombol_beli = driver.find_element("id", "beli")
assert tombol_beli.is_enabled()
assert tombol_beli.get_attribute("data-harga") == "250000"

is_displayed() validates the element is visible, is_enabled() validates the element is active, and get_attribute(...) reads an attribute value. These three reads are the raw materials for page state assertions.

Validating Behavior After Actions

The most valuable assertions check the effects of user actions:

PythonValidating action results
before = len(driver.find_elements("css selector", ".item"))
driver.find_element("id", "tambah").click()
wait = WebDriverWait(driver, 10)
wait.until(lambda d: len(d.find_elements("css selector", ".item")) == before + 1)
after = len(driver.find_elements("css selector", ".item"))
assert after == before + 1

The snippet above asserts that clicking the button adds one item. Note the wait before the assertion — assertions must wait for the state to stabilize, not check immediately.

Dynamic Assertions for Responsive UI

Responsive UI makes element appearance change depending on viewport size. Dynamic assertions validate the same behavior but through different representations:

PythonResponsive assertion
def cek_menu_terlihat(driver, mode):
    if mode == "desktop":
        return driver.find_element("id", "nav-desktop").is_displayed()
    return driver.find_element("id", "hamburger").is_displayed()
 
 
def test_menu_responsif():
    driver.set_window_size(1440, 900)
    assert cek_menu_terlihat(driver, "desktop")
    driver.set_window_size(375, 812)
    assert cek_menu_terlihat(driver, "mobile")

The cek_menu_terlihat(driver, mode) function picks the relevant element for each mode. The principle: validate the same goal (the menu is accessible), not a single specific implementation that can change with screen width.

Error Reporting and Screenshot on Failure

Automatic Screenshots on Failure

Finding the reason for a failure is much easier with visual evidence. Screenshots can be taken automatically via a pytest fixture that checks the test result:

PythonAutomatic screenshot in conftest.py
import pytest
from pathlib import Path
 
 
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
    outcome = yield
    report = outcome.get_result()
    if report.when == "call" and report.failed:
        driver = item.funcargs.get("driver")
        if driver:
            Path("artifacts").mkdir(exist_ok=True)
            driver.save_screenshot(f"artifacts/{item.name}.png")
            with open(f"artifacts/{item.name}.html", "w") as f:
                f.write(driver.page_source)

The pytest_runtest_makereport(item, call) hook captures the result of each test. On failure, the hook saves a screenshot and an HTML snapshot to the artifacts folder. These two files become the primary evidence when triaging failures.

Adding Context Messages

Besides screenshots, include context in the logs: the URL at failure time, browser version, and the last step executed. Combining screenshots plus context dramatically cuts debugging time.

Info

Save screenshots in PNG format and attach them as artifacts in CI. Don't write them into a committed project directory, because their contents change on every run.

Conclusion

Episode 7 completes the proper test cycle: action without validation is empty automation. You can now validate text, visibility, attributes, and behavior with pytest assertions, compose dynamic assertions for responsive UI, and automatically produce failure evidence in the form of screenshots and HTML snapshots.

Key takeaways:

  • Assertions turn automation into tests that truly validate.
  • Always write a message on asserts so failures are easy to trace.
  • Assert functional outcomes, not brittle implementation details.
  • Wait for stable state before asserting action results.
  • Automatic screenshots and HTML snapshots on failure speed up debugging.

In episode 8 next, we'll cover advanced browser actions — handling alerts, popups, frames, and windows, drag and drop, double click, hover, and keyboard actions, automating file upload and download, and scrolling and viewport handling.