Test Design Patterns

Back

Loading concept...

πŸ—οΈ Test Design Patterns in Selenium

Building Your Test Automation Like a Master Architect


🎬 The Story: Building a LEGO City

Imagine you’re building an amazing LEGO city. You could just throw bricks everywhere randomly… but that would be messy and hard to change later!

Smart builders organize their pieces:

  • 🏠 Houses in one box
  • πŸš— Cars in another box
  • 🌳 Trees in a third box

When you want to change something, you know exactly where to look!

Test Design Patterns work the same way. They help you organize your test code so it’s:

  • βœ… Easy to understand
  • βœ… Easy to change
  • βœ… Easy to fix when something breaks

πŸ“¦ Page Object Model Pattern

What Is It?

Think of your favorite restaurant menu. You don’t need to know how to cook the foodβ€”you just point at what you want!

Page Object Model (POM) is like creating a menu for each webpage:

  • The menu (Page Object) knows where everything is
  • Your tests just say β€œI want this” without worrying about details

Why Use It?

Without POM (Messy):
β”œβ”€β”€ Test 1: Find button by ID "login-btn"
β”œβ”€β”€ Test 2: Find button by ID "login-btn"
β”œβ”€β”€ Test 3: Find button by ID "login-btn"
└── Button ID changes? FIX 100 TESTS! 😱
With POM (Clean):
β”œβ”€β”€ LoginPage: knows the button location
β”œβ”€β”€ Test 1: Uses LoginPage
β”œβ”€β”€ Test 2: Uses LoginPage
└── Button ID changes? FIX 1 PLACE! πŸŽ‰

Real Example

# WITHOUT POM - Messy! πŸ™ˆ
def test_login():
    driver.find_element(
        By.ID, "username"
    ).send_keys("john")
    driver.find_element(
        By.ID, "password"
    ).send_keys("secret")
    driver.find_element(
        By.ID, "login-btn"
    ).click()

# WITH POM - Clean! ✨
def test_login():
    login_page.enter_username("john")
    login_page.enter_password("secret")
    login_page.click_login()

🎨 Page Class Design

The Blueprint

Every page in your website gets its own β€œhelper class.” Think of it like giving each room in your house a dedicated robot butler!

Three Golden Rules

graph TD A["🎯 Rule 1: One Page = One Class"] --> B["πŸ“ Rule 2: Store Locators Together"] B --> C["πŸ”§ Rule 3: Create Action Methods"] C --> D["βœ… Clean & Organized!"]

Simple Structure

class LoginPage:
    # πŸ“ Where things are
    USERNAME_FIELD = (By.ID, "username")
    PASSWORD_FIELD = (By.ID, "password")
    LOGIN_BUTTON = (By.ID, "login-btn")

    def __init__(self, driver):
        self.driver = driver

    # πŸ”§ What you can do
    def enter_username(self, username):
        self.driver.find_element(
            *self.USERNAME_FIELD
        ).send_keys(username)

Naming Tips

Element Type Good Name Bad Name
Button LOGIN_BUTTON btn1
Input EMAIL_INPUT field
Link FORGOT_PASSWORD_LINK a2

βš™οΈ Page Method Implementation

What Are Page Methods?

Page methods are the actions you can do on a page. Like pressing buttons on a TV remote!

TV Remote (Page Object):
β”œβ”€β”€ πŸ“Ί turnOn()
β”œβ”€β”€ πŸ”Š volumeUp()
β”œβ”€β”€ πŸ“Ί changeChannel(number)
└── ⏸️ pause()

Types of Methods

graph TD A["Page Methods"] --> B["🎬 Action Methods"] A --> C["❓ Query Methods"] A --> D["βœ… Verification Methods"] B --> E["click, type, submit"] C --> F["getText, getValue"] D --> G["isDisplayed, isEnabled"]

Complete Example

class ProductPage:
    ADD_TO_CART = (By.ID, "add-cart")
    PRODUCT_NAME = (By.CLASS, "name")
    CART_BADGE = (By.ID, "cart-count")

    # 🎬 Action Method
    def add_to_cart(self):
        self.driver.find_element(
            *self.ADD_TO_CART
        ).click()

    # ❓ Query Method
    def get_product_name(self):
        return self.driver.find_element(
            *self.PRODUCT_NAME
        ).text

    # βœ… Verification Method
    def is_added_to_cart(self):
        badge = self.driver.find_element(
            *self.CART_BADGE
        )
        return int(badge.text) > 0

Pro Tip: Method Chaining

Make your tests read like sentences!

# Chain methods together
checkout_page \
    .enter_address("123 Main St") \
    .select_shipping("Express") \
    .apply_coupon("SAVE10") \
    .place_order()

πŸ“Š Data-Driven Testing

The Problem

You want to test login with:

  • Valid username + valid password βœ…
  • Invalid username + valid password ❌
  • Valid username + invalid password ❌
  • Empty username + empty password ❌

Writing 4 separate tests? Too much copy-paste!

The Solution: One Test, Many Inputs

graph TD A["πŸ“‹ Test Data"] --> B["πŸ”„ Same Test Logic"] B --> C["Run with Data 1"] B --> D["Run with Data 2"] B --> E["Run with Data 3"] B --> F["Run with Data N..."]

Simple Example

# Test data - like a shopping list
test_data = [
    ("john", "pass123", True),
    ("wrong", "pass123", False),
    ("john", "wrong", False),
    ("", "", False),
]

# One test, many runs!
@pytest.mark.parametrize(
    "user,password,should_pass",
    test_data
)
def test_login(user, password, should_pass):
    login_page.login(user, password)

    if should_pass:
        assert "Welcome" in driver.title
    else:
        assert login_page.has_error()

Benefits

Without Data-Driven With Data-Driven
4 separate tests 1 test
40 lines of code 15 lines of code
Hard to add cases Easy to add cases
Lots of duplication Zero duplication

πŸ“ Test Data Management

Where Does Test Data Live?

Think of test data like ingredients for cooking. You need a good pantry!

graph TD A["πŸ“ Test Data Sources"] --> B["πŸ“„ CSV Files"] A --> C["πŸ“Š Excel Files"] A --> D["πŸ”§ JSON Files"] A --> E["πŸ—„οΈ Database"] A --> F["🌐 API Response"]

Option 1: JSON Files (Recommended for small data)

{
  "valid_users": [
    {
      "username": "john_doe",
      "password": "secure123",
      "role": "admin"
    },
    {
      "username": "jane_doe",
      "password": "pass456",
      "role": "user"
    }
  ]
}
import json

def load_test_data(filename):
    with open(filename) as f:
        return json.load(f)

users = load_test_data("users.json")

Option 2: CSV Files (Great for tables)

username,password,expected_result
john,pass123,success
invalid,pass123,error
john,wrong,error
import csv

def load_csv_data(filename):
    with open(filename) as f:
        reader = csv.DictReader(f)
        return list(reader)

Option 3: Environment-Specific Data

# Different data for different environments
data_files = {
    "dev": "data/dev_users.json",
    "staging": "data/staging_users.json",
    "prod": "data/prod_users.json"
}

env = os.getenv("TEST_ENV", "dev")
data = load_test_data(data_files[env])

Data Organization Structure

πŸ“ tests/
β”œβ”€β”€ πŸ“ data/
β”‚   β”œβ”€β”€ πŸ“„ users.json
β”‚   β”œβ”€β”€ πŸ“„ products.csv
β”‚   └── πŸ“„ config.json
β”œβ”€β”€ πŸ“ pages/
β”‚   β”œβ”€β”€ 🐍 login_page.py
β”‚   └── 🐍 product_page.py
└── πŸ“ tests/
    β”œβ”€β”€ 🐍 test_login.py
    └── 🐍 test_products.py

🎯 Putting It All Together

Here’s how all pieces work together:

graph TD A["πŸ“ Test Data Files"] --> B["πŸ“Š Data-Driven Test"] C["🏠 Page Objects"] --> B B --> D["πŸ”§ Page Methods"] D --> E["βœ… Test Results"]

Complete Working Example

# 1. Page Object with Methods
class LoginPage:
    USERNAME = (By.ID, "user")
    PASSWORD = (By.ID, "pass")
    SUBMIT = (By.ID, "login")
    ERROR = (By.CLASS, "error")

    def __init__(self, driver):
        self.driver = driver

    def login(self, user, pwd):
        self.driver.find_element(
            *self.USERNAME
        ).send_keys(user)
        self.driver.find_element(
            *self.PASSWORD
        ).send_keys(pwd)
        self.driver.find_element(
            *self.SUBMIT
        ).click()
        return self

    def has_error(self):
        try:
            self.driver.find_element(
                *self.ERROR
            )
            return True
        except:
            return False

# 2. Test Data (from JSON file)
test_cases = [
    ("valid@email.com", "pass123", True),
    ("invalid", "pass123", False),
]

# 3. Data-Driven Test
@pytest.mark.parametrize(
    "email,password,success",
    test_cases
)
def test_login_scenarios(
    driver, email, password, success
):
    page = LoginPage(driver)
    page.login(email, password)

    assert page.has_error() == (not success)

πŸš€ Quick Recap

Pattern Purpose Remember It As
Page Object Model Separate page info from tests β€œMenu for each page”
Page Class Design Organize locators & methods β€œRobot butler per room”
Page Methods Actions on pages β€œTV remote buttons”
Data-Driven Testing One test, many inputs β€œRecipe with ingredients list”
Test Data Management Organize your data β€œWell-organized pantry”

πŸ’‘ Final Wisdom

β€œGood tests are like good storiesβ€”they’re easy to read, easy to change, and everyone understands them.”

Start small:

  1. Create ONE page object
  2. Add 2-3 methods
  3. Use it in ONE test
  4. Celebrate! πŸŽ‰

Then repeat and grow your test automation empire! πŸ‘‘

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.