Bird
Raised Fist0
Selenium Pythontesting~15 mins

Page class structure in Selenium Python - Deep Dive

Choose your learning style10 modes available
Overview - Page class structure
What is it?
A Page class structure is a way to organize code that tests websites. It groups all the parts and actions of a single web page into one class. This makes tests easier to read and maintain because each page has its own clear set of instructions. It helps testers work faster and avoid repeating code.
Why it matters
Without a Page class structure, test code becomes messy and hard to fix. Testers would repeat the same steps many times, making mistakes more likely. When the website changes, fixing tests would take much longer. Using this structure saves time, reduces errors, and makes teamwork smoother.
Where it fits
Before learning this, you should know basic Python and how to use Selenium to find and interact with web elements. After this, you can learn about test frameworks like pytest and how to write full test cases using Page classes.
Mental Model
Core Idea
A Page class groups all the elements and actions of one web page into a single, reusable object to keep test code clean and easy to maintain.
Think of it like...
It's like having a toolbox for each room in your house. Instead of searching the whole house for a hammer, you go to the toolbox in the kitchen for kitchen tools or the toolbox in the garage for garage tools. Each toolbox holds everything you need for that room.
┌─────────────────────────────┐
│        Page Class           │
├─────────────┬───────────────┤
│ Elements    │ Actions       │
│ (locators)  │ (methods)     │
│             │               │
│ - Button    │ - click_button() │
│ - Textbox   │ - enter_text()   │
│ - Label     │ - get_label()    │
└─────────────┴───────────────┘
Build-Up - 7 Steps
1
FoundationUnderstanding Web Elements and Locators
🤔
Concept: Learn what web elements are and how to find them using locators in Selenium.
Web elements are parts of a web page like buttons, text boxes, or links. Selenium finds these using locators such as ID, name, class, or XPath. For example, driver.find_element(By.ID, 'submit') finds the submit button.
Result
You can identify and interact with parts of a web page in your test code.
Knowing how to find elements is the base for any web test automation.
2
FoundationBasic Python Classes for Grouping Code
🤔
Concept: Learn how to create simple Python classes to group related code together.
A Python class is like a blueprint for an object. It can hold data (variables) and actions (methods). For example, class Car: def start(self): print('Car started') groups the start action with the Car object.
Result
You can organize code into reusable blocks that represent real-world things.
Grouping related code into classes makes programs easier to understand and reuse.
3
IntermediateCreating a Page Class with Locators
🤔Before reading on: do you think locators should be inside or outside the Page class? Commit to your answer.
Concept: Put all locators for a page inside the Page class as variables to keep them organized.
Inside a Page class, define locators as class variables using tuples, e.g., USERNAME = (By.ID, 'username'). This keeps all element identifiers in one place, making updates easier when the page changes.
Result
All locators for a page are stored clearly inside one class.
Centralizing locators prevents duplication and simplifies maintenance when web pages update.
4
IntermediateAdding Methods for Page Actions
🤔Before reading on: should methods in a Page class interact directly with Selenium or return data? Commit to your answer.
Concept: Add methods in the Page class that perform actions on the page using the locators.
Methods like enter_username(self, name) use the locator USERNAME to find the element and send keys. This hides Selenium details from test scripts and provides clear actions like login_page.enter_username('user').
Result
Test code can call simple methods to interact with the page without repeating Selenium commands.
Encapsulating actions in methods makes tests cleaner and easier to read.
5
IntermediateUsing the Page Class in Test Scripts
🤔
Concept: Learn how to create and use Page class objects in your test code.
In test scripts, create an instance of the Page class and call its methods. For example, login_page = LoginPage(driver); login_page.enter_username('user'). This separates test logic from page details.
Result
Tests become shorter, clearer, and easier to maintain.
Separating test steps from page details reduces duplication and improves test clarity.
6
AdvancedHandling Page Navigation and Returns
🤔Before reading on: do you think a method that clicks a login button should return a new Page object or nothing? Commit to your answer.
Concept: Methods that cause page changes should return new Page class objects representing the new page.
For example, a login method clicks the button and returns a HomePage object. This models the real user flow and helps tests chain actions naturally.
Result
Test code can follow page flows by chaining method calls and Page objects.
Modeling page transitions as object returns mirrors real user navigation and improves test design.
7
ExpertDesigning for Maintainability and Scalability
🤔Before reading on: do you think all locators and methods belong in one big Page class or should they be split? Commit to your answer.
Concept: Split large pages into smaller components or use inheritance to share common code for better maintainability.
For complex pages, create component classes for repeated parts like menus or forms. Use base Page classes for shared methods. This reduces duplication and eases updates.
Result
Test code stays clean and scalable even as the application grows.
Breaking pages into components and using inheritance prevents code bloat and simplifies maintenance in large projects.
Under the Hood
The Page class holds locators as tuples and methods that use Selenium WebDriver to find elements and perform actions. When a method runs, it calls driver.find_element with the locator, interacts with the element, and may return data or new Page objects. This wraps Selenium calls inside Python objects, hiding complexity from test scripts.
Why designed this way?
This design was created to solve the problem of duplicated and fragile test code. By grouping page details and actions, it isolates changes to one place when the UI updates. Alternatives like writing raw Selenium in tests were error-prone and hard to maintain, so the Page class pattern became a best practice.
┌───────────────┐       ┌───────────────┐
│   Test Code   │──────▶│  Page Class   │
│ (calls methods│       │ (holds locators│
│  like login())│       │  and actions) │
└───────────────┘       └──────┬────────┘
                                │
                                ▼
                      ┌───────────────────┐
                      │ Selenium WebDriver│
                      │ (finds elements,  │
                      │  clicks, types)   │
                      └───────────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Is it okay to put test assertions inside Page class methods? Commit to yes or no.
Common Belief:Some think Page classes should include test checks and assertions to verify page state.
Tap to reveal reality
Reality:Page classes should only handle page interactions, not test assertions. Assertions belong in test scripts.
Why it matters:Mixing assertions into Page classes makes code harder to reuse and maintain, and blurs responsibilities.
Quick: Do you think storing locators as strings is better than tuples? Commit to your answer.
Common Belief:Some believe storing locators as plain strings is simpler and enough.
Tap to reveal reality
Reality:Locators should be stored as tuples like (By.ID, 'value') to work correctly with Selenium methods.
Why it matters:Using strings alone breaks locator usage and causes runtime errors.
Quick: Should Page classes directly create WebDriver instances? Commit to yes or no.
Common Belief:Some think Page classes should create and manage WebDriver instances themselves.
Tap to reveal reality
Reality:WebDriver should be created outside and passed into Page classes to keep code flexible and testable.
Why it matters:Creating WebDriver inside Page classes makes tests hard to control and reuse.
Quick: Do you think one big Page class per application is better than many small ones? Commit to your answer.
Common Belief:Some believe having one large Page class for the whole app is simpler.
Tap to reveal reality
Reality:Each page should have its own Page class to keep code organized and maintainable.
Why it matters:Big classes become confusing and hard to update, slowing down test development.
Expert Zone
1
Page classes often use lazy loading for elements, finding them only when needed to avoid stale element errors.
2
Using explicit waits inside Page methods improves test stability by waiting for elements to be ready before interacting.
3
Advanced Page classes can include logging and error handling to provide better debugging information during test failures.
When NOT to use
Page class structure is less useful for very simple or one-off tests where overhead is not justified. For API testing or non-UI tests, other patterns like service objects or direct calls are better.
Production Patterns
In real projects, Page classes are combined with test frameworks like pytest and continuous integration. They often use base classes for common features and component classes for reusable page parts. Tests call Page methods to perform flows, improving readability and reducing maintenance.
Connections
Object-Oriented Programming
Page classes are an application of OOP principles like encapsulation and abstraction.
Understanding OOP helps grasp why grouping page elements and actions into classes improves code structure and reuse.
Single Responsibility Principle (SRP)
Page classes follow SRP by having one reason to change: the web page they represent.
Knowing SRP explains why separating page logic from test logic reduces bugs and eases maintenance.
Human Factors in User Interface Design
Page classes model user interactions with UI elements, reflecting how humans navigate pages.
Understanding user behavior helps design Page methods that mimic real user actions, making tests more realistic.
Common Pitfalls
#1Mixing test assertions inside Page class methods.
Wrong approach:class LoginPage: def verify_login(self): assert 'Welcome' in self.driver.page_source # wrong: assertion inside page class
Correct approach:class LoginPage: def is_logged_in(self): return 'Welcome' in self.driver.page_source # In test script: assert login_page.is_logged_in() # assertion in test
Root cause:Confusing responsibilities between page interaction and test verification.
#2Hardcoding locators as strings without using Selenium's By.
Wrong approach:USERNAME = 'username' # wrong: missing locator type self.driver.find_element(USERNAME).send_keys('user')
Correct approach:from selenium.webdriver.common.by import By USERNAME = (By.ID, 'username') self.driver.find_element(*USERNAME).send_keys('user')
Root cause:Not understanding how Selenium expects locators as tuples.
#3Creating WebDriver inside Page class constructor.
Wrong approach:class LoginPage: def __init__(self): self.driver = webdriver.Chrome() # wrong: driver created inside page
Correct approach:class LoginPage: def __init__(self, driver): self.driver = driver # driver passed in from test
Root cause:Not separating test setup from page logic.
Key Takeaways
Page class structure organizes web page elements and actions into one place, making tests cleaner and easier to maintain.
Locators should be stored as tuples inside the Page class, and methods should perform actions without mixing test assertions.
Page classes should receive WebDriver instances from outside to keep code flexible and testable.
Modeling page navigation by returning new Page objects reflects real user flows and improves test design.
Splitting large pages into components and using inheritance helps maintainability in complex projects.

Practice

(1/5)
1. What is the main purpose of a page class in Selenium Python testing?
easy
A. To write assertions for test results
B. To store test data for multiple test cases
C. To execute the browser driver directly
D. To group web elements and actions for a single web page

Solution

  1. Step 1: Understand the role of page classes

    Page classes organize locators and methods related to one web page, making tests cleaner.
  2. Step 2: Identify the correct purpose

    Grouping elements and actions for a page matches the page class concept.
  3. Final Answer:

    To group web elements and actions for a single web page -> Option D
  4. Quick Check:

    Page class = group elements and actions [OK]
Hint: Page classes group elements and actions per page [OK]
Common Mistakes:
  • Confusing page class with test data storage
  • Thinking page class runs the browser driver
  • Mixing page class with assertion logic
2. Which of the following is the correct way to define a locator inside a Selenium page class in Python?
easy
A. submit_button = (By.ID, 'submit')
B. submit_button = 'By.ID, submit'
C. submit_button = By.ID('submit')
D. submit_button = By('ID', 'submit')

Solution

  1. Step 1: Recall locator tuple syntax

    Locators use tuples like (By.ID, 'value') to specify how to find elements.
  2. Step 2: Check each option

    submit_button = (By.ID, 'submit') correctly uses a tuple with By.ID and string 'submit'. Others have syntax errors or wrong formats.
  3. Final Answer:

    submit_button = (By.ID, 'submit') -> Option A
  4. Quick Check:

    Locator = (By.METHOD, 'value') tuple [OK]
Hint: Use tuple (By.METHOD, 'value') for locators [OK]
Common Mistakes:
  • Using string instead of tuple for locator
  • Calling By.ID as a function
  • Incorrect tuple structure
3. Given the following page class snippet, what will page.get_title() return if the browser's current page title is "Welcome Page"?
class HomePage:
    def __init__(self, driver):
        self.driver = driver
    def get_title(self):
        return self.driver.title

page = HomePage(driver)
medium
A. An error because get_title is missing
B. "Welcome Page"
C. None
D. "HomePage"

Solution

  1. Step 1: Understand get_title method

    The method returns self.driver.title, which is the browser's current page title.
  2. Step 2: Check given title

    The browser's title is "Welcome Page", so the method returns this string.
  3. Final Answer:

    "Welcome Page" -> Option B
  4. Quick Check:

    driver.title returns current page title [OK]
Hint: driver.title gives current page title string [OK]
Common Mistakes:
  • Assuming method returns None by default
  • Thinking method is missing or undefined
  • Confusing class name with page title
4. Identify the error in this page class method and how to fix it:
class LoginPage:
    def __init__(self, driver):
        self.driver = driver
        self.username_input = (By.ID, 'username')
    def enter_username(self, username):
        self.driver.find_element(self.username_input).send_keys(username)
medium
A. Missing return statement in enter_username method
B. send_keys should be called on driver, not element
C. find_element expects two arguments, fix by unpacking locator: *self.username_input
D. Locator should be a string, not a tuple

Solution

  1. Step 1: Check find_element usage

    find_element expects two arguments: method and locator string, not a tuple as one argument.
  2. Step 2: Fix by unpacking tuple

    Use *self.username_input to unpack tuple into two arguments.
  3. Final Answer:

    find_element expects two arguments, fix by unpacking locator: *self.username_input -> Option C
  4. Quick Check:

    Use * to unpack locator tuple in find_element [OK]
Hint: Unpack locator tuple with * in find_element [OK]
Common Mistakes:
  • Passing locator tuple as single argument
  • Calling send_keys on driver instead of element
  • Thinking return is required for action methods
5. You want to create a page class for a login page with username and password fields and a login button. Which design best follows the page class structure and good practices?
hard
A. class LoginPage: def __init__(self, driver): self.driver = driver self.username = (By.ID, 'user') self.password = (By.ID, 'pass') self.login_btn = (By.ID, 'login') def enter_username(self, name): self.driver.find_element(*self.username).send_keys(name) def enter_password(self, pwd): self.driver.find_element(*self.password).send_keys(pwd) def click_login(self): self.driver.find_element(*self.login_btn).click()
B. class LoginPage: def __init__(self, driver): self.driver = driver def login(self, user, pwd): self.driver.find_element(By.ID, 'user').send_keys(user) self.driver.find_element(By.ID, 'pass').send_keys(pwd) self.driver.find_element(By.ID, 'login').click()
C. class LoginPage: def __init__(self, driver): self.driver = driver self.username = 'user' self.password = 'pass' self.login_btn = 'login' def login(self, user, pwd): self.driver.find_element(By.ID, self.username).send_keys(user) self.driver.find_element(By.ID, self.password).send_keys(pwd) self.driver.find_element(By.ID, self.login_btn).click()
D. class LoginPage: def __init__(self, driver): self.driver = driver def enter_username(self, name): self.driver.find_element(By.ID, 'user').send_keys(name) def enter_password(self, pwd): self.driver.find_element(By.ID, 'pass').send_keys(pwd) def click_login(self): self.driver.find_element(By.ID, 'login').click()

Solution

  1. Step 1: Check locator storage

    class LoginPage: def __init__(self, driver): self.driver = driver self.username = (By.ID, 'user') self.password = (By.ID, 'pass') self.login_btn = (By.ID, 'login') def enter_username(self, name): self.driver.find_element(*self.username).send_keys(name) def enter_password(self, pwd): self.driver.find_element(*self.password).send_keys(pwd) def click_login(self): self.driver.find_element(*self.login_btn).click() stores locators as tuples in __init__, which is best practice for reuse and clarity.
  2. Step 2: Check method structure

    class LoginPage: def __init__(self, driver): self.driver = driver self.username = (By.ID, 'user') self.password = (By.ID, 'pass') self.login_btn = (By.ID, 'login') def enter_username(self, name): self.driver.find_element(*self.username).send_keys(name) def enter_password(self, pwd): self.driver.find_element(*self.password).send_keys(pwd) def click_login(self): self.driver.find_element(*self.login_btn).click() has separate methods for each action, improving readability and maintainability.
  3. Step 3: Compare other options

    class LoginPage: def __init__(self, driver): self.driver = driver def login(self, user, pwd): self.driver.find_element(By.ID, 'user').send_keys(user) self.driver.find_element(By.ID, 'pass').send_keys(pwd) self.driver.find_element(By.ID, 'login').click() repeats locators in methods, class LoginPage: def __init__(self, driver): self.driver = driver self.username = 'user' self.password = 'pass' self.login_btn = 'login' def login(self, user, pwd): self.driver.find_element(By.ID, self.username).send_keys(user) self.driver.find_element(By.ID, self.password).send_keys(pwd) self.driver.find_element(By.ID, self.login_btn).click() stores locators as strings only, class LoginPage: def __init__(self, driver): self.driver = driver def enter_username(self, name): self.driver.find_element(By.ID, 'user').send_keys(name) def enter_password(self, pwd): self.driver.find_element(By.ID, 'pass').send_keys(pwd) def click_login(self): self.driver.find_element(By.ID, 'login').click() does not store locators as variables.
  4. Final Answer:

    Option A follows page class structure and good practices best -> Option A
  5. Quick Check:

    Store locators once, use methods for actions [OK]
Hint: Store locators as tuples, use methods for actions [OK]
Common Mistakes:
  • Repeating locators inside methods
  • Storing locators as plain strings
  • Not separating actions into methods
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard