I have Options table with ON/OFF option (checkboxes, radio buttons and text fields). The table has 30 rows. I stored locators as ENUM
class OptionType(Enum):
PAYMENT_TYPE_CREDIT_CARD = "Credit_Card"
PAYMENT_TYPE_ECP = "ECP"
PAYMENT_TYPE_SAFETECH = "SAFETECH"
PAYMENT_TYPE_ON = "yPaymentType"
PAYMENT_TYPE_OFF = "nPaymentType"
USE_POST_MESSAGE_ON = "yPostMessage"
USE_POST_MESSAGE_OFF = "nPostMessage"
ORDER_ABSTRACTION_ON = "yuID"
ORDER_ABSTRACTION_OFF = "nuID"
What is the best way to add methods to access those locators and make some actions over them? If I create the methods for each element in the same class it will be too long (more than 300 lines)
I tried to store elements directly by using XPATH and only elements take more than 300 lines. I need methods for actions as well.
Locators should be stored as By
. That's the class designed to contain locators in Selenium, e.g.
username_locator = (By.ID, "username")
which can then be used like
driver.find_element(username_locator).send_keys("username")
For the other part, you should research the page object model. It's the generally accepted best practice for organizing your code. A page object is a class that contains all locators and methods for a given page (or portion of a page, e.g. header, footer, left or top nav, etc.) that is designed to be reusable.
All locators for that page should be stored in the page object as By
. Each action that a user can take on that page should be represented as a method in the page object. This organization structure makes maintenance much cleaner and clearer.
A simple example
There's a test page here that simulates a login page. Below I've created a sample page object for the login page and a sample script using the Login page page object.
login_page.py, the page object
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
class LoginPage:
'''Page object for the login page'''
def __init__(self, driver: webdriver):
self.driver = driver
self.password_locator = (By.ID, 'password')
self.username_locator = (By.ID, 'username')
self.login_button_locator = (By.CSS_SELECTOR, "button")
def login(self, username: str, password: str):
'''Logs in with the provided credentials'''
wait = WebDriverWait(self.driver, 10)
wait.until(EC.visibility_of_element_located(self.username_locator)).send_keys(username)
wait.until(EC.visibility_of_element_located(self.password_locator)).send_keys(password)
wait.until(EC.element_to_be_clickable(self.login_button_locator)).click()
pom_test.py, the script that uses the page object
from selenium import webdriver
from page_object_model.login_page import LoginPage
URL = 'https://the-internet.herokuapp.com/login'
driver = webdriver.Chrome()
driver.maximize_window()
driver.get(URL)
USERNAME = "tomsmith"
PASSWORD = "SuperSecretPassword!"
login_page = LoginPage(driver)
login_page.login(USERNAME, PASSWORD)
driver.quit()
Hopefully that helps make sense of the page object model.
In general, don't worry about how many lines of code are in your page object. If it's a really large, complex page then you're going to have a lot of code in there. Just make sure you're only including methods and locators that correspond to only that page object. Each page should have it's own page object... keeping those carefully separated will help reduce the amount of code in each class.