What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Use one class for each meaningful page or reusable UI component, pass it a Selenium WebDriver, and expose methods that describe user actions. Keep assertions in the test, where expected behavior belongs, and use explicit waits for the conditions your test needs. This structure keeps page-specific selectors and interactions in one place, so a UI change is less likely to require edits across many tests.
What the Page Object Model does
The Page Object Model (POM) is a way to organize browser tests around the web pages and interface regions they use. A page object owns knowledge of its page’s locators and provides useful operations such as entering credentials or submitting a search. A test uses those operations to describe a scenario and checks whether the expected outcome occurred.
Selenium’s Page Object Models guidance explains that this reduces duplicated code and localizes fixes when the UI changes. A page object is not a second test case: its job is to interact with the UI, not to decide whether the application’s behavior passed acceptance criteria.
Set up Selenium with Python
Install Selenium in the Python environment used by your project:
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →#1 Best Overall
python -m pip install selenium
Recent Selenium releases can manage compatible browser drivers through Selenium Manager, but the browser itself must be installed. If your environment blocks driver downloads or requires a pinned browser/driver, configure that separately according to your project’s setup.
The examples below use Python 3, Selenium’s current By locator style, and pytest. Install pytest if it is not already available:
python -m pip install pytest
Build a page object around user actions
Suppose a login page has elements with stable IDs username, password, and submit, and a successful login displays an element with ID welcome. Adapt these locators and the expected result to your application’s actual markup.
# pages/login_page.py
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
class LoginPage:
URL = "https://example.com/login"
USERNAME = (By.ID, "username")
PASSWORD = (By.ID, "password")
SUBMIT = (By.ID, "submit")
WELCOME = (By.ID, "welcome")
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 10)
def open(self):
self.driver.get(self.URL)
self.wait.until(EC.visibility_of_element_located(self.USERNAME))
return self
def login_as(self, username, password):
self.wait.until(EC.visibility_of_element_located(self.USERNAME)).send_keys(username)
self.driver.find_element(*self.PASSWORD).send_keys(password)
self.wait.until(EC.element_to_be_clickable(self.SUBMIT)).click()
def welcome_message(self):
return self.wait.until(EC.visibility_of_element_located(self.WELCOME)).text
The class receives an existing driver rather than creating its own browser. That keeps browser lifecycle and test setup under the test runner’s control. The open() method checks that the login form is ready before returning; login_as() performs a user-level operation; and welcome_message() exposes observable state for the test to verify.
Returning self from open() is optional. A workflow may instead return a different page object after navigation, a component object, or no value. Choose the return style that makes the scenario readable; Selenium’s page-object guidance does not require a particular project layout or method convention.
Rank #2
Keep behavioral assertions in the test
Here is a pytest test using the page object. The example assumes the application provides a test account through environment variables and that the successful-login message is stable.
# tests/test_login.py
import os
from selenium import webdriver
from pages.login_page import LoginPage
def test_user_can_log_in():
driver = webdriver.Chrome()
try:
page = LoginPage(driver).open()
page.login_as(
os.environ["TEST_USERNAME"],
os.environ["TEST_PASSWORD"],
)
assert "Welcome" in page.welcome_message()
finally:
driver.quit()
Run it with credentials set in your shell, for example:
TEST_USERNAME='test-user' TEST_PASSWORD='test-password' pytest -q
Do not put real credentials in source control. In a larger suite, create the driver in a pytest fixture so setup and cleanup are shared consistently. Keep the assertion in the test: Selenium’s guidance says page objects should not make ordinary verifications or assertions. A narrow readiness check, such as confirming the expected form is visible when opening a page, is different from asserting that a business outcome succeeded.
Choose locators the page can own
Keep each locator beside the page or component that describes it. A short page class can define locator tuples as class attributes, as above; a project with many locators may use a separate locator class or module. The important point is that separating files should make ownership clearer, not scatter UI knowledge across the test suite.
Selenium supports locator strategies including ID, name, CSS selector, link text, partial link text, class name, tag name, and XPath. See the official locator strategies reference. Prefer an application-provided stable test attribute when one is available; otherwise choose a locator that is both clear and resilient to incidental markup changes. No locator strategy is universally best.
Rank #3
- Use a page method such as
login_as()when it expresses a meaningful user task. - Avoid methods that only rename one low-level call, such as
click_submit_button(), unless that abstraction adds useful meaning or coordination. - When a locator changes, update the page or component that owns it rather than duplicating a replacement across tests.
Wait for the condition the next action needs
A navigation command returning does not guarantee that JavaScript-rendered content is ready. Selenium’s Waiting Strategies documentation describes race conditions caused by asynchronous page changes as a primary source of flaky tests. Use WebDriverWait(driver, seconds).until(...) with a condition tied to the next operation: presence or visibility before reading, clickability before clicking, or another condition that reflects the UI state you need.
In the example, the username field is awaited before typing, the submit control is awaited before clicking, and the welcome element is awaited before its text is read. The ten-second timeout is an example project choice, not a Selenium requirement; set a consistent timeout appropriate to the application and test environment.
Avoid using fixed sleeps as normal synchronization: they may waste time when a page is fast and still fail when it is slower than expected. Also avoid casually combining implicit and explicit waits. Selenium’s waits guidance warns that mixing them can produce unpredictable timing behavior; choose a consistent wait policy and make it explicit in test setup.
Extract a component when it earns its own object
A repeated navigation bar, product card, or substantial form region can be represented as a component object when it has meaningful operations or appears on multiple pages. The containing page can compose that object rather than growing into one class that represents the entire site.
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
class MainNavigation:
SEARCH = (By.CSS_SELECTOR, "nav input[name='q']")
SEARCH_BUTTON = (By.CSS_SELECTOR, "nav button[type='submit']")
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 10)
def search_for(self, term):
field = self.wait.until(EC.visibility_of_element_located(self.SEARCH))
field.clear()
field.send_keys(term)
self.wait.until(EC.element_to_be_clickable(self.SEARCH_BUTTON)).click()
class HomePage:
def __init__(self, driver):
self.driver = driver
self.navigation = MainNavigation(driver)
Use component objects for coherent regions with reusable behavior, not for every small fragment. Excessively fine-grained classes add indirection without necessarily reducing maintenance.
Rank #4
Or skip the browser setup
A Selenium page object is for browser-driven tests and interactions. If your separate need is to capture a page image or PDF as visual evidence, ScreenshotNeo is a screenshot API and MCP server; it does not replace Selenium’s page-object pattern or your assertions. For example, this cURL request saves a screenshot:
Recommended Free Tools
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo API documentation for request options. It accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots.
Sign up for ScreenshotNeo’s free plan to try 1,000 screenshots a month with no card.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Troubleshooting common POM failures
NoSuchElementException or a missing element
Check that the locator matches the current page, that the test opened the intended URL, and that the element is not inside an iframe or shadow root requiring additional handling. For asynchronously rendered elements, wait for the relevant presence or visibility condition instead of searching immediately.
ElementNotInteractableException or click interception
The element may be hidden, disabled, covered by an overlay, or not yet ready. Wait for visibility or clickability as appropriate, and inspect the page state and locator. Do not treat a JavaScript click as a routine workaround; it can bypass the user interaction your test is meant to exercise.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problemsTests pass locally but fail intermittently in CI
Look for race conditions, shared browser state, unstable locators, and tests depending on leftover data or execution order. Replace broad timing assumptions with waits for the actual state, ensure each test creates or receives a clean driver, and retain failure evidence such as screenshots or browser logs where your test infrastructure supports them.
Best Value
Unexpected delays or timeouts
Identify which condition timed out and whether the page actually reached that state. A timeout should not be hidden by increasing it blindly: first check navigation, test data, selectors, overlays, and application errors, then adjust the project’s consistent wait policy if the legitimate operation needs longer.
Further reading
- Selenium: Page object models
- Selenium: Waiting Strategies
- Selenium: Locator strategies
- Selenium Python Bindings: Page Objects — an example of page classes, locator organization, and test flow; its layout is illustrative, not mandatory.
Frequently Asked Questions
Should a page object contain assertions?
Keep behavioral assertions in the test. A page object may check that its expected page is ready during initialization, but it should not decide whether the scenario passed.
Do I need a separate locator class or a pages package?
No. Locators can live on the page class for a small project, or in separate modules if that improves clarity. Choose organization that keeps UI knowledge easy to find.
Free tools Windows power users keep installed
One-click scans. No signup required.
Should every UI element have its own component object?
No. Extract a component when it represents a coherent region with meaningful reusable behavior; trivial fragments often do not justify an extra class.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




