Location>code7788 >text

Selenium+pytest page object model framework

Popularity:692 ℃/2025-04-11 14:41:32

Download address:/xiaopo1998/web_ui_test.git

Selenium Page Object Model Framework Instructions
This framework implements a modular and easy-to-maintain Page Object Model architecture based on Selenium WebDriver, separating different types of operations into different classes according to responsibilities.

Framework structure
The framework is designed in a layered manner and consists of the following main components:

core/
├── base_page.py # Basic page class, including access to core operation methods and other components
├── element_actions.py # element waiting and check operations
├── action_chains.py # Advanced Mouse and Keyboard Operations
├── browser_operations.py # window, frame and warning box operations
├── browser_utils.py # Cookie, JavaScript and screenshots and other auxiliary functions
├── select_operations.py # drop-down box specific operation
├── # WebDriver Management
└── # Log Management
Component Description

  1. BasePage (base_page.py)
    The basic page class is the core of the entire framework, responsible for providing basic page operation methods and serving as an entry point for accessing other dedicated components. It contains the following core features:

Provide basic element search methods
Provide common element operation methods (click, enter, get text, etc.)
Responsible for instantiating and providing access to all dedicated components
2. ElementActions (element_actions.py)
Element waiting and check operation classes are specifically responsible for handling element visibility, existence and other inspection and wait operations. Main functions include:

Provide various elements waiting methods (waiting to be visible, waiting to be clickable, etc.)
Provide element status check method (whether it exists, is visible, etc.)
Provide text and attribute-related waiting methods
3. ActionChainOperations (action_chains.py)
The advanced operation class of mouse and keyboard, responsible for handling complex interactions. Main functions include:

Mouse operation (hover, drag and drop, right-click, etc.)
Keyboard operation (keys, key combinations, etc.)
Other advanced interactive operations
4. BrowserOperations (browser_operations.py)
The browser operation class is responsible for handling operations such as windows, frames and warning boxes. Main functions include:

Window operation (switch, close, get handle, etc.)
Frame operation (switch frames, return to main document, etc.)
Warning box operation (accept, cancel, get text, etc.)
Page navigation (forward, backward, refresh, etc.)
5. BrowserUtils (browser_utils.py)
Browser tool class, providing auxiliary functions such as cookie management, JavaScript execution and screenshots. Main functions include:

Cookie management (get, add, delete, etc.)
JavaScript execution (scrolling pages, etc.)
Screenshot function (full page screenshots, element screenshots, etc.)
6. SelectOperations (select_operations.py)
The drop-down box operation class is specially used to handle the operations of drop-down box elements. Main functions include:

Select options (by text, value, index, etc.)
Get selected option information
Multiple selection drop-down box operation (select multiple, deselect, etc.)
How to use
initialization
First, you need to initialize the BasePage object, which is the core entry point of the framework:

from selenium import webdriver
from core.base_page import BasePage

Initialize WebDriver

driver = ()

Create BasePage instance

page = BasePage(driver)

Navigate to page

page.navigate_to("")
Basic element operation
The BasePage class provides the most basic element operation method:

Define element locator

from import By
username_locator = (, "username")
password_locator = (, "password")
login_button_locator = (, "login")

Find elements

username_element = page.find_element(username_locator)

Basic element operation

page.input_text(username_locator, "test_user")
page.input_text(password_locator, "password123")
(login_button_locator)

Get element text

text = page.get_text((, "welcome_message"))
print(text)

Get element attributes

value = page.get_attribute((, "username"), "value")
Element waiting and checking
Access element waiting and check functions through element attribute:

Wait for the element to be visible

.wait_for_element_visible((, "dashboard"))

Check if the element exists

if .is_element_present((, "error_message")):
print("Login failed")

Wait for the text to appear

.wait_for_text_present((, "status"), "success")

Highlight elements (useful for debugging)

.highlight_element((By.LINK_TEXT, "User Settings"))

Waiting for the element to be invisible

.wait_for_element_not_visible((, "loading"))

Scroll to element position

.scroll_to_element((, "footer"))
Mouse and keyboard operation
Access the advanced mouse and keyboard actions through the actions property:

from import Keys

Mouse hover

.mouse_hover((, "menu"))

Right-click

.right_click((, "context_menu"))

double click

.double_click((, "double_click_element"))

Drag and drop operation

.drag_and_drop((, "source"), (, "target"))

Key operation

.press_key((, "search_box"), )

Key combination

.press_key_sequence((, "text_editor"), , "a")

Click and hold

.click_and_hold((, "slider"))

release

()

Move the mouse

.move_by_offset(10, 20)
Browser windows and framework operations
Access windows, frames, and warning boxes through browser properties:

Window Operation

original_handle = .get_current_window_handle()
.open_new_tab("/other_page")
.switch_to_window(window_index=0) # Switch back to the original window
.close_current_window()

Framework Operation

.switch_to_frame((, "iframe_id"))
.switch_to_default_content()
.switch_to_parent_frame()

Warning box operation

.accept_alert()
alert_text = .get_alert_text()
.dismiss_alert()
.send_text_to_alert("Input Content")

Navigation operations

.refresh_page()
.go_back()
.go_forward()

Get the page source code

html = .get_page_source()

Wait for the page to load

.wait_for_page_load()
Cookie and JavaScript Operations
Access cookies, JavaScript and screenshot functions through the utils attribute:

Cookie operation

cookies = .get_cookies()
cookie = .get_cookie("session_id")
.add_cookie({"name": "session", "value": "123456"})
.delete_cookie("old_cookie")
.delete_all_cookies()

JavaScript Operations

.scroll_to_top()
.scroll_to_bottom()
.scroll_by(0, 500) # Scroll down 500 pixels

screenshot

screenshot_path = .take_screenshot("login_page.png")
element_screenshot = .take_element_screenshot((, "error"), "")
Pull down box operation
Accessing the drop-down box special operation through the select property:

Select the drop-down box option

.select_by_text((, "country"), "China")
.select_by_value((, "country"), "CN")
.select_by_index((, "country"), 0)

Get the selected option

selected_text = .get_selected_text((, "country"))
selected_value = .get_selected_value((, "country"))

Get all options

all_options = .get_all_options((, "country"))
all_option_texts = .get_all_options_text((, "country"))
all_option_values = .get_all_options_value((, "country"))

Determine whether the drop-down box supports multiple selections

is_multiple = .is_multiple((, "multi_select"))

Multiple selection drop-down box operation

if .is_multiple((, "multi_select")):
.select_by_text((, "multi_select"), "Option 1")
.select_by_text((, "multi_select"), "Option 2")
.deselect_by_text((, "multi_select"), "Option 1")
.deselect_all((, "multi_select"))
Custom page class
In actual projects, it is recommended to create a subclass inherited from BasePage for each page:

from core.base_page import BasePage
from import By

class LoginPage(BasePage):
# Define page element locator
USERNAME_INPUT = (, "username")
PASSWORD_INPUT = (, "password")
LOGIN_BUTTON = (, "login")
ERROR_MESSAGE = (, "error")

def login(self, username, password):
     """
     Login operation
     :param username: Username
     :param password: Password
     """
     self.input_text(self.USERNAME_INPUT, username)
     self.input_text(self.PASSWORD_INPUT, password)
     (self.LOGIN_BUTTON)
    
 def get_error_message(self):
     """
     Get error message
     :return: Error message text
     """
     if .is_element_visible(self.ERROR_MESSAGE):
         return self.get_text(self.ERROR_MESSAGE)
     return None
    
 def wait_for_login_success(self):
     """
     Wait for login to succeed
     """
     .wait_for_element_not_visible(self.ERROR_MESSAGE)
     .wait_for_page_load()

Then you can use it like this:

Initialize the page

login_page = LoginPage(driver)
login_page.navigate_to("/login")

Perform login

login_page.login("user123", "password123")

Check results

error = login_page.get_error_message()
if error:
print(f"Login failed: {error}")
else:
print("Login successfully")
Best Practices
Organize code by page: Create a class inherited from BasePage for each page
Packaging business operations: encapsulating multiple basic operations into meaningful business methods
Use explicit wait: Always use the wait method to ensure elements are ready
Keep locators centrally defined in page classes: easy to maintain
Use the appropriate component: select the appropriate component according to the operation type (element, actions, browser, utils, select)
Use reasonable naming: method names and variable names should clearly indicate their role
Add detailed comments: Add comment instructions for complex operations
Handling exceptions: Catch and handle possible exceptions
Exception handling
Most methods in the framework log and throw exceptions (such as TimeoutException). It is recommended to catch these exceptions in the test code:

try:
((, "non_existent_button"))
except Exception as e:
print(f"Operation failed: {e}")
.take_screenshot("")
Extended framework
To add new features, you can extend an existing class or create a new operation class:

Extend the ElementActions class

from core.element_actions import ElementActions
from import WebDriverWait

class ExtendedElementActions(ElementActions):
def wait_for_element_count(self, locator, count, timeout=None):
"""
Wait for the number of elements to reach the expected value
:param locator: element locator
:param count: The expected number of elements
:param timeout: timeout
"""
timeout = timeout or ['browser']['implicit_wait']
(f"Waiting for the number of elements {locator} to reach {count}")

    def check_count(driver):
        elements = driver.find_elements(*locator)
        return len(elements) == count
        
    WebDriverWait(, timeout).until(check_count)

Frequently Asked Questions
The element cannot be found

Make sure to use the correct locator
Check if the element is visible in the page
Consider using the wait method to ensure that the element is loaded
The operation has no effect

Check if the element is blocked by other elements
Ensure elements are interactive
Try to perform an operation using JavaScript
Frame switching issues

After operating elements in the iframe, remember to switch back to the default content
Nested iframes need to be switched layer by layer
Multi-window operation

After switching to a new window, if you need to return to the original window, remember to save the handle of the original window
Note that the handle list will change after closing the window
Element selection problem

If a locator matches multiple elements, find_element returns the first matching element
More precise positioning strategies can be used or multiple positioning conditions can be combined
More examples
Form operation

Loading form page

page.navigate_to("/form")

Enter text

page.input_text((, "first_name"), "Zhang")
page.input_text((, "last_name"), "three")

Select the drop-down box

.select_by_text((, "country"), "China")

Select the radio button

((By.CSS_SELECTOR, "input[name='gender'][value='male']"))

Check the check box

((, "agreement"))

Submit a form

((By.CSS_SELECTOR, "button[type='submit']"))

Wait for the results

.wait_for_text_present((, "result"), "submit successful")
Table operations

Get all rows in the table

rows = page.find_elements((By.CSS_SELECTOR, "table tr"))

Traversal

for row in rows[1:]: # Skip the header
# Get the cell
cells = row.find_elements_by_tag_name("td")
if len(cells) > 0:
print(f"First column: {cells[0].text}, second column: {cells[1].text}")

# Click the button in the row
 if len(cells) > 3:
     edit_button = cells[3].find_element_by_tag_name("button")
     edit_button.click()
     break

File upload
import os

Upload file

file_path = ("./test_file.txt")
page.input_text((, "file_input"), file_path)

Click the Upload button

((, "upload_button"))

Wait for uploading to complete

.wait_for_text_present((, "upload_status"), "upload successful")
Test case example
Full login-search-purchase process
Here is a complete e-commerce website test case showing how to integrate all the knowledge points:

import unittest
from selenium import webdriver
from import TimeoutException

from pages.login_page import LoginPage
from pages.home_page import HomePage
from pages.product_page import ProductPage
from pages.cart_page import CartPage
from pages.checkout_page import CheckoutPage
import time

class ShoppingTest():

def setUp(self):
     # Initialize WebDriver
      = ()
     .maximize_window()
    
     # Initialize all page objects
     self.login_page = LoginPage()
     self.home_page = HomePage()
     self.product_page = ProductPage()
     self.cart_page = CartPage()
     self.checkout_page = CheckoutPage()
    
 def tearDown(self):
     # Test ends, close the browser
     if :
         ()

 @screenshot_on_failure # Use the decorator defined earlier
 def test_purchase_flow(self):
     """Test the complete purchase process"""
     # Test data
     username = "test_user"
     password = "Test@123"
     product_name = "Test product"
    
     # 1. Log in
     self.login_page.navigate_to("/login")
     self.login_page.wait_for_page_load()
     self.login_page.login(username, password)
    
     # Verify login successfully
     user_welcome = self.home_page.get_welcome_message()
     (username, user_welcome, "The username does not appear after login")
    
     # 2. Search for products
     self.home_page.search_product(product_name)
    
     # Verify search results
     search_count = self.home_page.get_search_results_count()
     (search_count, 0, "search no results")
    
     # 3. Open product details
     self.home_page.click_first_product()
    
     # Verify product details page
     product_title = self.product_page.get_product_title()
     (product_name, product_title, f"Product details page does not match, expect to include '{product_name}'")
    
     # 4. Add to cart
     self.product_page.add_to_cart()
    
     # 5. View the cart
     self.product_page.go_to_cart()
    
     # Verify the shopping cart
     cart_items = self.cart_page.get_cart_items()
     (len(cart_items), 1, "Car is empty")
    
     # 6. Enter the checkout process
     self.cart_page.proceed_to_checkout()
    
     # 7. Fill in the receipt information
     self.checkout_page.fill_shipping_info({
         "name": "Zhang San",
         "phone": "13800138000",
         "address": "Six-six-six-street in Haidian District, Beijing",
         "zipcode": "100000"
     })
    
     # 8. Select payment method
     self.checkout_page.select_payment_method("cod") # cash on delivery
    
     # 9. Submit an Order
     order_number = self.checkout_page.place_order()
    
     # Verify that the order is submitted successfully
     success_message = self.checkout_page.get_success_message()
     ("Order has been submitted successfully", success_message)
     (order_number, "Order number not generated")
    
     # 10. Snap the screenshot of the successful page
     self.checkout_page.utils.take_screenshot(f"order_success_{order_number}.png")
    
     print(f"Test completed, order number: {order_number}")

if name == "main":
()