Advanced Chapter 20 · 13 min read

Best Practices & Design Patterns

Master test automation best practices — retry logic, test independence, data management, flaky test handling, and advanced design patterns.

Best Practices & Design Patterns

Writing Selenium tests is easy. Writing maintainable, reliable, fast Selenium tests that scale — that's the real skill. These best practices come from years of industry experience and will save you countless hours of debugging.

Golden Rules

  • Tests must be independent — no test should depend on another test's state
  • Use explicit waits — never Thread.sleep, never implicit + explicit together
  • Keep tests fast — use API calls for setup/teardown, not UI
  • One assertion theme per test — test one behavior, not everything
  • Clean up after yourself — delete test data, reset state
RetryAnalyzer.java
package com.selenium.course.utils;

import org.testng.IRetryAnalyzer;
import org.testng.ITestResult;

// Retry flaky tests automatically
public class RetryAnalyzer implements IRetryAnalyzer {

    private int retryCount = 0;
    private static final int MAX_RETRIES = 2;

    @Override
    public boolean retry(ITestResult result) {
        if (retryCount < MAX_RETRIES) {
            retryCount++;
            System.out.println("Retrying test: " + result.getName()
                + " (attempt " + (retryCount + 1) + ")");
            return true;
        }
        return false;
    }
}

// Usage: @Test(retryAnalyzer = RetryAnalyzer.class)
// public void testFlaky() { ... }
BestPracticesTest.java
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.*;
import org.testng.Assert;
import org.testng.annotations.*;
import java.time.Duration;

public class BestPracticesTest {

    WebDriver driver;
    WebDriverWait wait;

    @BeforeMethod
    public void setup() {
        driver = new ChromeDriver();
        // Use explicit waits, not implicit
        wait = new WebDriverWait(driver, Duration.ofSeconds(10));
    }

    // GOOD: Single responsibility
    @Test
    public void testFormFieldAcceptsInput() {
        driver.get("https://www.selenium.dev/selenium/web/web-form.html");
        WebElement input = wait.until(
            ExpectedConditions.elementToBeClickable(By.name("my-text")));
        input.sendKeys("Test value");
        Assert.assertEquals(input.getAttribute("value"), "Test value");
    }

    // GOOD: Use helper methods to reduce duplication
    private WebElement waitAndClick(By locator) {
        WebElement element = wait.until(
            ExpectedConditions.elementToBeClickable(locator));
        element.click();
        return element;
    }

    private void waitAndType(By locator, String text) {
        WebElement element = wait.until(
            ExpectedConditions.visibilityOfElementLocated(locator));
        element.clear();
        element.sendKeys(text);
    }

    // GOOD: Descriptive test names
    @Test
    public void shouldDisplayErrorWhenSubmittingEmptyForm() {
        driver.get("https://www.selenium.dev/selenium/web/web-form.html");
        waitAndClick(By.cssSelector("button[type='submit']"));
        // Verify error state
    }

    // GOOD: Use @DataProvider for multiple scenarios
    @DataProvider(name = "validInputs")
    public Object[][] validInputs() {
        return new Object[][] {
            { "Simple text" },
            { "Text with numbers 123" },
            { "Special chars !@#" },
        };
    }

    @Test(dataProvider = "validInputs")
    public void shouldAcceptVariousInputTypes(String input) {
        driver.get("https://www.selenium.dev/selenium/web/web-form.html");
        waitAndType(By.name("my-text"), input);
        WebElement field = driver.findElement(By.name("my-text"));
        Assert.assertEquals(field.getAttribute("value"), input);
    }

    @AfterMethod
    public void teardown() { if (driver != null) driver.quit(); }
}
utils/retry.py
"""Retry decorator for flaky tests"""
import functools, time

def retry(max_attempts=3, delay=1):
    """Retry a test function on failure"""
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            last_exception = None
            for attempt in range(1, max_attempts + 1):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    last_exception = e
                    if attempt < max_attempts:
                        print(f"Retry {attempt}/{max_attempts}: {e}")
                        time.sleep(delay)
            raise last_exception
        return wrapper
    return decorator

# Or use pytest-rerunfailures plugin:
# pip install pytest-rerunfailures
# pytest --reruns 2 --reruns-delay 1
test_best_practices.py
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import pytest

@pytest.fixture
def driver():
    d = webdriver.Chrome()
    yield d
    d.quit()


# Helper functions — reduce duplication
def wait_and_click(driver, locator, timeout=10):
    element = WebDriverWait(driver, timeout).until(
        EC.element_to_be_clickable(locator))
    element.click()
    return element


def wait_and_type(driver, locator, text, timeout=10):
    element = WebDriverWait(driver, timeout).until(
        EC.visibility_of_element_located(locator))
    element.clear()
    element.send_keys(text)
    return element


# GOOD: Single responsibility
def test_form_field_accepts_input(driver):
    driver.get("https://www.selenium.dev/selenium/web/web-form.html")
    el = wait_and_type(driver, (By.NAME, "my-text"), "Test value")
    assert el.get_attribute("value") == "Test value"


# GOOD: Descriptive names
def test_should_display_error_when_submitting_empty_form(driver):
    driver.get("https://www.selenium.dev/selenium/web/web-form.html")
    wait_and_click(driver, (By.CSS_SELECTOR, "button[type='submit']"))


# GOOD: Parametrize for multiple scenarios
@pytest.mark.parametrize("input_text", [
    "Simple text",
    "Text with numbers 123",
    "Special chars !@#",
])
def test_should_accept_various_input_types(driver, input_text):
    driver.get("https://www.selenium.dev/selenium/web/web-form.html")
    wait_and_type(driver, (By.NAME, "my-text"), input_text)
    field = driver.find_element(By.NAME, "my-text")
    assert field.get_attribute("value") == input_text


# GOOD: Marks for selective execution
@pytest.mark.smoke
def test_critical_page_loads(driver):
    driver.get("https://www.selenium.dev/")
    assert "Selenium" in driver.title

# Run: pytest -m smoke
# Run: pytest --reruns 2  (with pytest-rerunfailures)

Selenium Advanced Best Practices & Design Patterns

Written by PV

© 2026 All Rights Reserved