{"id":73563,"date":"2025-03-01T12:52:02","date_gmt":"2025-03-01T07:22:02","guid":{"rendered":"https:\/\/www.guvi.in\/blog\/?p=73563"},"modified":"2026-06-09T09:59:34","modified_gmt":"2026-06-09T04:29:34","slug":"python-selenium-page-object-model-explained","status":"publish","type":"post","link":"https:\/\/www.guvi.in\/blog\/python-selenium-page-object-model-explained\/","title":{"rendered":"From Zero to Hero: Python Selenium Page Object Model Explained"},"content":{"rendered":"\n<p>Have you ever written a Selenium test that worked perfectly at first, but broke the moment a button, input field, or page layout changed? That is one of the biggest challenges in test automation. As web applications grow, automation scripts can quickly become messy, repetitive, and difficult to maintain.<\/p>\n\n\n\n<p>The <strong>Page Object Model<\/strong>, or <strong>POM<\/strong>, solves this problem by giving your Selenium framework a clean structure. It separates test logic from page-level actions, so your tests do not directly depend on raw locators everywhere. Instead, each page is represented as a class with its own elements and reusable methods.<\/p>\n\n\n\n<p>In this article, you will learn how <strong>Python, Selenium, and Page Object Model<\/strong> work together to build a maintainable automation framework. We will cover the core concept, benefits, project structure, code implementation, and best practices so you can write cleaner Selenium tests that are easier to scale and debug.<\/p>\n\n\n\n<p><strong>Quick Answer:<\/strong> <\/p>\n\n\n\n<p>The Page Object Model in Python Selenium is a design pattern that separates test logic from page-level UI actions. Each web page is created as a class, where locators and reusable methods are stored together. This makes Selenium test scripts cleaner, easier to maintain, and more scalable as the application grows. Instead of repeating <code>find_element()<\/code>, <code>click()<\/code>, and <code>send_keys()<\/code> in every test case, testers can call readable methods like <code>enter_username()<\/code>, <code>enter_password()<\/code>, and <code>click_login()<\/code>. This reduces code duplication and makes automation frameworks easier to debug and update.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>What is the Page Object Model?<\/strong><\/h2>\n\n\n\n<p>The Page Object Model is a design pattern used in <a href=\"https:\/\/www.guvi.in\/blog\/what-is-automation-testing\/\" target=\"_blank\" rel=\"noreferrer noopener\">automation testing<\/a> to represent web pages as objects. Each web page in the application under test is modeled as a class, with the class containing the web elements (such as buttons, text fields, and links) and the methods that perform actions on those elements (such as clicking a button or entering text).<\/p>\n\n\n\n<p>The primary purpose of Page Object Model is to separate the test logic from the <a href=\"https:\/\/www.guvi.in\/blog\/what-is-user-interface\/\" target=\"_blank\" rel=\"noreferrer noopener\">UI structure<\/a>. This separation makes the test code more readable and maintainable. When the UI changes, you only need to update the page object class, not the individual test cases.<\/p>\n\n\n\n<p>For example, consider a login page with username and password fields and a submit button. In POM, you would create a LoginPage class that contains locators for these elements and methods like enter_username(), enter_password(), and click_submit(). Your test cases would then use these methods instead of directly interacting with the web elements.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Benefits of the Page Object Model<\/strong><\/h2>\n\n\n\n<p>The Page Object Model offers several compelling advantages:-<\/p>\n\n\n\n<ol>\n<li><strong>Improved Maintainability:<\/strong> When the UI changes, you only need to update the page object class, not the test cases. This reduces the effort required to keep your test suite up-to-date.<br><\/li>\n\n\n\n<li><strong>Reduced Code Duplication:<\/strong> By encapsulating common actions in methods, you avoid repeating the same code in multiple test cases.<br><\/li>\n\n\n\n<li><strong>Enhanced Readability: <\/strong>Test cases become more readable and easier to understand because they focus on the test logic rather than the details of the UI interactions.<br><\/li>\n\n\n\n<li><strong>Reusability:<\/strong> Page objects can be reused across multiple test cases, promoting code reuse and consistency.<br><\/li>\n\n\n\n<li><strong>Better Organization:<\/strong> Page Object Model encourages a structured approach to organizing your test code, making it easier to manage large test suites.<\/li>\n<\/ol>\n\n\n\n<p>These benefits make Page Object Model an essential tool for anyone looking to build a robust and scalable test automation framework.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Why Use the Page Object Model in Selenium with Python?<\/strong><\/h2>\n\n\n\n<p><a href=\"https:\/\/www.guvi.in\/hub\/python\/\" target=\"_blank\" rel=\"noreferrer noopener\">Python<\/a>\u2019s simplicity and readability make it an excellent choice for writing test automation scripts. When paired with <a href=\"https:\/\/www.guvi.in\/blog\/selenium-essentials\/\" target=\"_blank\" rel=\"noreferrer noopener\">Selenium<\/a>, it allows for powerful and flexible automation. However, as your test suite expands, managing the code can become cumbersome without a proper structure. Page Object Model addresses this challenge by providing a clear and organized way to structure your test code. It allows you to:-&nbsp;<\/p>\n\n\n\n<ul>\n<li>Abstract UI Details: By hiding the locators and implementation details within page objects, your test cases can focus on the test scenarios rather than the specifics of the UI.<br><\/li>\n\n\n\n<li>Facilitate Collaboration: POM makes it easier for team members to understand and contribute to the test automation code, even if they are not deeply familiar with the UI.<br><\/li>\n\n\n\n<li>Support Parallel Development: Developers can work on the application while testers work on the automation scripts, with minimal conflicts, as long as the page objects are kept in sync with the UI changes. In the context of Selenium with Python, Page Object Model leverages Python\u2019s object-oriented features to create a clean and efficient automation framework.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Implementing Page Object Model in Python Selenium<\/strong><\/h2>\n\n\n\n<p>To illustrate the implementation of POM, let\u2019s consider a simple example. The Project structure is given as below :-&nbsp;<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/lh7-rt.googleusercontent.com\/docsz\/AD_4nXdmH0sp7We4djb2f_9Z4v-Ic4wvJILgYKiUlwaQrzxpqzy-oveitM-HwyNQ0bxpO4W_jXRWLkvD85Um9RvphaM6eRLVS62E0ACxmGFHFxIM4-PmQY_Qj1XR8Hw_HZtuTNjeH4OA?key=oDd9Y8paeUbyuKXNS1fjaNkw\" alt=\"Implementing Page Object Model in Python Selenium\" title=\"\"><\/figure>\n\n\n\n<p>The project structure is designed to adhere to the principles of the Page Object Model and best practices for test automation. It separates concerns into distinct categories, ensuring a clean, modular, and maintainable framework:<br><\/p>\n\n\n\n<ul>\n<li><strong>Page Objects<\/strong> (in PageObjects): Encapsulate the UI structure and interactions, making the code reusable and easier to maintain when the UI changes. Each page object represents a specific page or component of the application, abstracting its locators and interactions.<br><\/li>\n\n\n\n<li><strong>Tests<\/strong> (in Tests): Focus on the test logic and assertions, using the page objects to interact with the application. This separation ensures that tests are concise and focused on verification, not UI manipulation.<br><\/li>\n\n\n\n<li><strong>Common Utilities<\/strong> (in common.py): Provide shared logic or helper functions that are not specific to any page or test, such as Selenium helper methods or logging utilities.<br><\/li>\n\n\n\n<li><strong>Pytest Configuration<\/strong> (in conftest.py): Centralize the setup and configuration for the testing framework, such as fixtures for WebDriver management and hooks for customizing test execution.<\/li>\n<\/ul>\n\n\n\n<p><br><strong>Modularity and Reusability<\/strong>: The use of directories and __init__.py files allows for modular imports, making it easy to extend the framework by adding new page objects and test cases. For example, if the application grows to include additional pages (e.g., a dashboard or settings page), new page objects can be added to the PageObjects directory, and corresponding tests can be added to the Tests directory.<\/p>\n\n\n\n<p><strong>Scalability<\/strong>: As the application grows, new page objects and test files can be added to the respective directories without disrupting the existing structure. This ensures that the framework remains maintainable and scalable, even for large-scale test automation projects.<\/p>\n\n\n\n<p><strong>Root Directory<\/strong>:<\/p>\n\n\n\n<ul>\n<li>common.py for shared utilities.<\/li>\n\n\n\n<li>conftest.py for Pytest fixtures and configuration.<\/li>\n<\/ul>\n\n\n\n<p><strong>PageObjects Directory<\/strong>:<\/p>\n\n\n\n<ul>\n<li>base_page.py for common page object functionality.<\/li>\n\n\n\n<li>login_page.py for login-specific page interactions.<\/li>\n\n\n\n<li>__init__.py to enable package imports.<\/li>\n<\/ul>\n\n\n\n<p><strong>Tests Directory<\/strong>:<\/p>\n\n\n\n<ul>\n<li>test_login.py for login-related test cases.<\/li>\n\n\n\n<li>__init__.py to enable package imports.<\/li>\n<\/ul>\n\n\n\n<p>Now, let us see the code and understand its working:-&nbsp;<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># PageObjects\/base_page.py\n\n\"\"\"\n\nThe BasePage Class includes common methods like find elements, clicks and interact with UI\n\n\"\"\"\n\nfrom selenium.webdriver.support.ui import WebDriverWait\n\nfrom selenium.webdriver.support import expected_conditions as EC\n\nfrom selenium.common.exceptions import TimeoutException\n\nfrom selenium.common.exceptions import ElementNotVisibleException\n\nfrom selenium.common.exceptions import NoSuchElementException\n\nfrom common import Config\n\nclass BasePage:\n\n&nbsp;&nbsp;&nbsp;def __init__(self, driver):\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.driver = driver\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.timeout = Config().TIMEOUT\n\n&nbsp;&nbsp;&nbsp;def find_element(self, locator):\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try:\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;web_element = WebDriverWait(self.driver, self.timeout).until(EC.presence_of_element_located(locator))\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return web_element\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;except (NoSuchElementException, ElementNotVisibleException, TimeoutException):\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise Exception(f\"Element {locator} not found within {self.timeout} seconds\")\n\n&nbsp;&nbsp;&nbsp;def is_visible(self, locator):\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try:\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;web_element = WebDriverWait(self.driver, self.timeout).until(EC.visibility_of_element_located(locator))\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return web_element\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;except (NoSuchElementException, ElementNotVisibleException, TimeoutException):\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return False\n\n&nbsp;&nbsp;&nbsp;def is_enabled(self, locator):\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try:\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;web_element = WebDriverWait(self.driver, self.timeout).until(EC.element_to_be_clickable(locator))\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return web_element\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;except (NoSuchElementException, ElementNotVisibleException, TimeoutException):\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return False\n\n&nbsp;&nbsp;&nbsp;def click(self, locator):\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;element = self.find_element(locator) and self.is_visible(locator) and self.is_enabled(locator)\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;element.click()\n\n&nbsp;&nbsp;&nbsp;def enter_text(self, locator, text):\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;element = self.find_element(locator)\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;element.click()\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;element.send_keys(text)<\/code><\/pre>\n\n\n\n<p>This code defines a BasePage class, which includes several common methods used to interact with web elements during automated browser testing using the Selenium library. These methods handle tasks such as finding elements, checking visibility and enablement, clicking elements, and entering text.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Components of <code>base_page.py<\/code><\/strong><\/h3>\n\n\n\n<ol>\n<li><strong>Imports<\/strong>:\n<ul>\n<li>WebDriverWait, expected_conditions (EC): For waiting until certain conditions are met.<\/li>\n\n\n\n<li>TimeoutException, ElementNotVisibleException, NoSuchElementException: Handle exceptions related to element interactions.<\/li>\n\n\n\n<li>Config: Custom configuration class.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>BasePage Class<\/strong>:\n<ul>\n<li><strong>Constructor<\/strong>:\n<ul>\n<li>__init__(self, driver): Initializes the class with a web driver instance and sets a timeout value from the configuration.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Methods<\/strong>:\n<ul>\n<li>find_element(self, locator): Waits for an element to be present and returns it. Raises an exception if the element is not found within the timeout period.<\/li>\n\n\n\n<li>is_visible(self, locator): Checks if an element is visible and returns the element if it is. Returns False if the element is not found or not visible within the timeout period.<\/li>\n\n\n\n<li>is_enabled(self, locator): Checks if an element is clickable and returns the element if it is. Returns False if the element is not found or not clickable within the timeout period.<\/li>\n\n\n\n<li>click(self, locator): Clicks on an element after ensuring it is present, visible, and enabled.<\/li>\n\n\n\n<li>enter_text(self, locator, text): Finds an element, clicks on it, and enters the specified text.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<p>This code defines a LoginPage class that inherits from both BasePage and Locators classes. The class provides methods to interact with the login page elements, such as entering a username, entering a password, and clicking the login button. The Locators class contains the locators for the input fields and the submit button.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Components<\/strong><\/h3>\n\n\n\n<ol>\n<li><strong>Imports<\/strong>:\n<ul>\n<li>By: For specifying the type of locator (e.g., name, XPath).<\/li>\n\n\n\n<li>BasePage: Base class for page objects containing common methods.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Locators Class<\/strong>:\n<ul>\n<li><strong>Attributes<\/strong>:\n<ul>\n<li>USERNAME_INPUT_BOX: Locator for the username input box.<\/li>\n\n\n\n<li>PASSWORD_INPUT_BOX: Locator for the password input box.<\/li>\n\n\n\n<li>SUBMIT_BUTTON: Locator for the submit button.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>LoginPage Class<\/strong>:\n<ul>\n<li><strong>Attributes<\/strong>:\n<ul>\n<li>USERNAME_INPUT: Tuple containing the locator strategy and locator value for the username input field.<\/li>\n\n\n\n<li>PASSWORD_INPUT: Tuple containing the locator strategy and locator value for the password input field.<\/li>\n\n\n\n<li>LOGIN_BUTTON: Tuple containing the locator strategy and locator value for the login button.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Methods<\/strong>:\n<ul>\n<li>enter_username(self, username): Enters the specified username in the username input field by calling the enter_text method from the BasePage class.<\/li>\n\n\n\n<li>enter_password(self, password): Enters the specified password in the password input field by calling the enter_text method from the BasePage class.<\/li>\n\n\n\n<li>click_login(self): Clicks the login button by calling the click method from the BasePage class.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code># common.py\n\n\"\"\"\n\nCommon\/Basic Configuration file for the web application\n\n\"\"\"\n\nclass Config:\n\n&nbsp;&nbsp;&nbsp;BASE_URL = \"https:\/\/opensource-demo.orangehrmlive.com\/web\/index.php\/auth\/login\"\n\n&nbsp;&nbsp;&nbsp;DASHBOARD_URL = \"https:\/\/opensource-demo.orangehrmlive.com\/web\/index.php\/dashboard\/index\"\n\n&nbsp;&nbsp;&nbsp;TIMEOUT = 10\n\n&nbsp;&nbsp;&nbsp;USERNAME = \"Admin\"\n\n&nbsp;&nbsp;&nbsp;PASSWORD = \"admin123\"<\/code><\/pre>\n\n\n\n<p>This code defines a Config class that holds common configuration settings for a web application. These configurations include URLs, timeout values, and credentials used for logging into the web application.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Components of <code>common.py<\/code><\/h3>\n\n\n\n<ol>\n<li><strong>Config Class<\/strong>:\n<ul>\n<li><strong>Attributes<\/strong>:\n<ul>\n<li>BASE_URL: The base URL for the login page of the web application.<\/li>\n\n\n\n<li>DASHBOARD_URL: The URL for the dashboard page of the web application.<\/li>\n\n\n\n<li>TIMEOUT: The time (in seconds) to wait for a web element to become available before timing out.<\/li>\n\n\n\n<li>USERNAME: The username used for logging into the web application.<\/li>\n\n\n\n<li>PASSWORD: The password used for logging into the web application.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code># conftest.py\n\n\"\"\"\n\nUse conftest.py is used to Manage the WebDriver setup and teardown\n\nCentralizes WebDriver setup making it reusable across your tests\n\n\"\"\"\n\nimport pytest\n\nfrom selenium import webdriver\n\nfrom selenium.webdriver.chrome.service import Service\n\nfrom webdriver_manager.chrome import ChromeDriverManager\n\n@pytest.fixture(scope=\"function\")\n\ndef driver():\n\n&nbsp;&nbsp;&nbsp;\"\"\"Setup WebDriver\"\"\"\n\n&nbsp;&nbsp;&nbsp;driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))\n\n&nbsp;&nbsp;&nbsp;driver.maximize_window()\n\n&nbsp;&nbsp;&nbsp;\"\"\"Test Executes Here\"\"\"\n\n&nbsp;&nbsp;&nbsp;yield driver\n\n&nbsp;&nbsp;&nbsp;\"\"\"Cleanup after Test\"\"\"\n\n&nbsp;&nbsp;&nbsp;driver.quit()<\/code><\/pre>\n\n\n\n<p>This code manages the setup and teardown of the WebDriver for browser automation testing. By using conftest.py, you centralize the WebDriver setup, making it reusable across multiple test cases in your test suite. This is achieved using the pytest framework&#8217;s fixture mechanism.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Components of <code>conftest.py<\/code><\/strong><\/h3>\n\n\n\n<ol>\n<li><strong>Imports<\/strong>:\n<ul>\n<li>pytest: For defining fixtures used in tests.<\/li>\n\n\n\n<li>webdriver: Selenium WebDriver for browser automation.<\/li>\n\n\n\n<li>Service: Service class to manage the browser driver.<\/li>\n\n\n\n<li>ChromeDriverManager: Automatically manages the installation and setup of the ChromeDriver.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Fixture<\/strong>:\n<ul>\n<li>driver(): A pytest fixture that sets up the WebDriver, maximizes the browser window, executes the test, and then quits the WebDriver after the test is completed.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Steps in the Fixture<\/strong><\/h3>\n\n\n\n<ol>\n<li><strong>Setup WebDriver<\/strong>:\n<ul>\n<li>The WebDriver for Chrome is initialized using the ChromeDriverManager to automatically handle the setup.<\/li>\n\n\n\n<li>The browser window is maximized for the test.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Yield Statement<\/strong>:\n<ul>\n<li>The yield statement temporarily returns control back to the test case where the fixture is being used.<\/li>\n\n\n\n<li>The test case will execute after the yield statement.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Cleanup after Test<\/strong>:\n<ul>\n<li>After the test case execution, control returns to the fixture, and the WebDriver is quit to clean up resources.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code># Tests\/test_login.py\n\n\"\"\"\n\nCreate test using Pytest and Page Object Model\n\nFocuses purely on Test Logic leveraging the Structured Page Objects\n\nCommands :\n\npytest -v test_login.py\n\n\"\"\"\n\nimport pytest\n\nfrom PageObjects.login_page import LoginPage\n\nfrom common import Config\n\nfrom conftest import driver\n\ndef test_valid_login(driver):\n\n&nbsp;&nbsp;&nbsp;driver.get(Config().BASE_URL)\n\n&nbsp;&nbsp;&nbsp;login_page = LoginPage(driver)\n\n&nbsp;&nbsp;&nbsp;login_page.enter_username(Config().USERNAME)\n\n&nbsp;&nbsp;&nbsp;login_page.enter_password(Config().PASSWORD)\n\n&nbsp;&nbsp;&nbsp;login_page.click_login()\n\n&nbsp;&nbsp;&nbsp;# Assert some Post-Login Behavior\n\n&nbsp;&nbsp;&nbsp;assert Config().DASHBOARD_URL == driver.current_url\n\n&nbsp;&nbsp;&nbsp;print(f\"SUCCESS : Login Success with {Config().USERNAME} and {Config().PASSWORD}\")<\/code><\/pre>\n\n\n\n<p>This code creates a test case for the login functionality using the <a href=\"https:\/\/pytest.org\/\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">Pytest framework<\/a> and the Page Object Model (POM) design pattern. The test is designed to validate a successful login to the web application.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Components of <code>test_login.py<\/code><\/h3>\n\n\n\n<ol>\n<li><strong>Imports<\/strong>:\n<ul>\n<li>pytest: For defining and running tests.<\/li>\n\n\n\n<li>LoginPage: Page Object class representing the login page.<\/li>\n\n\n\n<li>Config: Configuration settings for the web application.<\/li>\n\n\n\n<li>driver: Pytest fixture that sets up the WebDriver, defined in conftest.py.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Test Function<\/strong>:\n<ul>\n<li>test_valid_login(driver): This function performs the following steps:\n<ol>\n<li><strong>Navigate to the Base URL<\/strong>:\n<ul>\n<li>driver.get(Config().BASE_URL): Opens the login page using the base URL from the configuration.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Initialize LoginPage Object<\/strong>:\n<ul>\n<li>login_page = LoginPage(driver): Creates an instance of the LoginPage class, which provides methods to interact with the login page elements.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Enter Username and Password<\/strong>:\n<ul>\n<li>login_page.enter_username(Config().USERNAME): Enters the username defined in the configuration.<\/li>\n\n\n\n<li>login_page.enter_password(Config().PASSWORD): Enters the password defined in the configuration.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Click the Login Button<\/strong>:\n<ul>\n<li>login_page.click_login(): Clicks the login button to submit the login form.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Assert Post-Login Behavior<\/strong>:\n<ul>\n<li>assert Config().DASHBOARD_URL == driver.current_url: Verifies that the current URL matches the expected dashboard URL after a successful login.<\/li>\n\n\n\n<li>print(f&#8221;SUCCESS : Login Success with {Config().USERNAME} and {Config().PASSWORD}&#8221;): Prints a success message to the console<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<p>In case you want to learn more about Python Selenium in Automation Testing, consider enrolling for HCL GUVI&#8217;s <a href=\"https:\/\/www.guvi.in\/zen-class\/selenium-automation-testing-course\/\" data-type=\"link\" data-id=\"https:\/\/www.guvi.in\/zen-class\/selenium-automation-testing-course\/\" target=\"_blank\" rel=\"noreferrer noopener\"><\/a><a href=\"https:\/\/www.guvi.in\/zen-class\/selenium-automation-testing-course\/?utm_source=blog&amp;utm_medium=hyperlink&amp;utm_campaign=python-selenium-page-object-model-explained\" target=\"_blank\" rel=\"noreferrer noopener\">Selenium Automation Testing Course<\/a> which teaches you everything from scratch by providing an industry-grade certificate!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Best Practices for Writing Page Objects in Python Selenium<\/h2>\n\n\n\n<p>A good Page Object Model is not only about creating one class for each page. It is about keeping the test framework clean, reusable, and easy to update when the UI changes. Poorly written page objects can become just as difficult to maintain as unstructured Selenium scripts.<\/p>\n\n\n\n<p>Here are the most important Page Object Model best practices:<\/p>\n\n\n\n<ul>\n<li><strong>Keep locators separate from test logic:<\/strong> Store locators inside page classes or a dedicated locator file. Test cases should not directly use XPath, CSS selectors, IDs, or class names.<\/li>\n\n\n\n<li><strong>Use clear action methods:<\/strong> Page methods should describe user actions, such as <code>login_as_user()<\/code>, <code>enter_username()<\/code>, or <code>click_submit_button()<\/code>. This keeps test cases readable and business-focused.<\/li>\n\n\n\n<li><strong>Avoid assertions inside page objects:<\/strong> Page objects should perform actions and return page information. Assertions should usually stay inside test files, because tests are responsible for validation.<\/li>\n\n\n\n<li><strong>Use explicit waits instead of sleep:<\/strong> <code>time.sleep()<\/code> makes tests slow and unreliable. Selenium\u2019s <code>WebDriverWait<\/code> should be used to wait for visibility, clickability, URL changes, or element presence.<\/li>\n\n\n\n<li><strong>Return the next page object after navigation:<\/strong> When clicking a login button opens a dashboard page, the method can return a <code>DashboardPage<\/code> object. This makes test flow cleaner and easier to chain.<\/li>\n\n\n\n<li><strong>Do not overload one page class:<\/strong> A page object should represent one page or one reusable component. Large pages can be split into smaller component objects such as header, sidebar, modal, or form classes.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Advanced Page Object Model Enhancements for Scalable Automation<\/h2>\n\n\n\n<p>A basic Page Object Model works well for small projects, but larger automation suites need more structure. As test coverage grows, the framework should support better reporting, debugging, reusable data, and cross-browser execution.<\/p>\n\n\n\n<p>Here are useful enhancements for a scalable Selenium POM framework:<\/p>\n\n\n\n<ul>\n<li><strong>Add a separate locator layer:<\/strong> A dedicated <code>locators.py<\/code> file can keep all element selectors organized. This helps testers update locators quickly when the UI changes.<\/li>\n\n\n\n<li><strong>Use environment-based configuration:<\/strong> Different test environments such as QA, staging, and production should have separate URLs, credentials, and browser settings.<\/li>\n\n\n\n<li><strong>Capture screenshots on failure:<\/strong> Automatic screenshots help debug failed tests faster. They are especially useful in <a href=\"https:\/\/www.guvi.in\/blog\/understanding-ci-cd\/\" target=\"_blank\" rel=\"noreferrer noopener\">CI\/CD pipelines<\/a> where tests run without manual observation.<\/li>\n\n\n\n<li><strong>Add structured logging:<\/strong> Logs can show which step failed, which locator was used, and what action was being performed. This makes failure analysis easier.<\/li>\n\n\n\n<li><strong>Support multiple browsers:<\/strong> A scalable framework should allow Chrome, Firefox, Edge, or headless browser execution through configuration.<\/li>\n\n\n\n<li><strong>Use test data management:<\/strong> Test data can be stored in JSON, YAML, CSV, Excel, or fixtures. This separates test scenarios from hardcoded input values.<\/li>\n\n\n\n<li><strong>Integrate with CI\/CD pipelines:<\/strong> Selenium tests can be triggered automatically through Jenkins, GitHub Actions, GitLab CI, or Azure DevOps after every deployment.<\/li>\n\n\n\n<li><strong>Generate test reports:<\/strong> Tools like Allure Report, Pytest <a href=\"https:\/\/www.guvi.in\/blog\/html-tutorial-guide-for-web-development\/\" target=\"_blank\" rel=\"noreferrer noopener\">HTML<\/a>, or built-in CI reports help teams understand pass rate, failure reasons, execution time, and flaky tests.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Conclusion<\/strong><\/h2>\n\n\n\n<p>In conclusion, the <strong>Page Object Model<\/strong> is a powerful design pattern that can significantly improve the quality and maintainability of your Selenium test automation scripts. By encapsulating the UI elements and actions within page object classes, you create a clear separation between test logic and UI interactions. <\/p>\n\n\n\n<p>This leads to cleaner, more readable, and more maintainable test code. In this introduction, we\u2019ve explored what POM is, its benefits, and how to implement it in Python with Selenium. We\u2019ve also provided a simple example to illustrate the concept. <\/p>\n\n\n\n<p>As you delve deeper into test automation, adopting the Page Object Model will help you manage complexity and scale your test suite effectively.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">FAQs<\/h2>\n\n\n<div id=\"rank-math-faq\" class=\"rank-math-block\">\n<div class=\"rank-math-list \">\n<div id=\"faq-question-1780346954886\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \">What is Page Object Model in Selenium Python?<\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Page Object Model in Selenium Python is a design pattern that stores page locators and page actions inside separate classes. It makes automation tests cleaner, reusable, and easier to maintain.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1780346971153\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \">Why is Page Object Model used in Selenium automation?<\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Page Object Model is used in Selenium automation to separate test logic from UI interaction code. This reduces code duplication and makes test scripts easier to update when the web page changes.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1780347005653\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \">Is Page Object Model good for large Selenium test suites?<\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Yes. Page Object Model is useful for large Selenium test suites because it improves structure, scalability, readability, and debugging. It helps teams manage multiple pages and test cases more efficiently.<\/p>\n\n<\/div>\n<\/div>\n<\/div>\n<\/div>","protected":false},"excerpt":{"rendered":"<p>Have you ever written a Selenium test that worked perfectly at first, but broke the moment a button, input field, or page layout changed? That is one of the biggest challenges in test automation. As web applications grow, automation scripts can quickly become messy, repetitive, and difficult to maintain. The Page Object Model, or POM, [&hellip;]<\/p>\n","protected":false},"author":60,"featured_media":73740,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[706,717,40],"tags":[],"views":"6120","authorinfo":{"name":"Vaishali","url":"https:\/\/www.guvi.in\/blog\/author\/vaishali\/"},"thumbnailURL":"https:\/\/www.guvi.in\/blog\/wp-content\/uploads\/2025\/02\/Page-Object-Model-300x112.webp","_links":{"self":[{"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/posts\/73563"}],"collection":[{"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/users\/60"}],"replies":[{"embeddable":true,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/comments?post=73563"}],"version-history":[{"count":11,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/posts\/73563\/revisions"}],"predecessor-version":[{"id":115466,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/posts\/73563\/revisions\/115466"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/media\/73740"}],"wp:attachment":[{"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/media?parent=73563"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/categories?post=73563"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/tags?post=73563"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}