In our last episode, we automated the login test. Today we are gonna learn how can we the registration testing. Almost every web application has a registration feature. The most important testing part in registration is validation. Of course, we also need to test if it successfully works or not. In most cases, we have a username and email, sometimes both, the password and confirm password fields. Though some registration requires the phone number, country, etc. But in our target application we have these:
- Username
- Password
- Confirm Password
- Phone Number
Let’s see their validation rules.
Username: Alphanumeric and required
Email: Generic email format and required
Password: Minimum 8 character and must be consist of one uppercase, one lowercase, and one number. It’s also required
Phone: Generic string field and not required.
Test Cases
- Submit the registration form without giving any input and check the backend validation message is showing properly or not.
- Try to register with an invalid email and see if the HTML validation message is showing or not.
- Try with an invalid password and check the backend validation message is showing properly or not.
- Typing different password and confirm password value and check the backend validation message.
- Registration is working perfectly after giving every input properly and also need to check the success message.
The Automation Testing Application Structure
. ├── application │ ├── app.py │ ├── __init__.py │ └── utils │ ├── constants.py │ ├── helpers.py │ ├── __init__.py ├── homepage │ ├── homepage.py │ ├── __init__.py ├── login │ ├── __init__.py │ ├── login.py ├── README.md ├── registration │ ├── __init__.py │ └── registration.py ├── requirements.txt └── settings.py └── .env
[ Step:01 ] Environment Variables
Add these credentials in our .env file.
REGISTRATION_USER_USERNAME=testuser100 REGISTRATION_USER_EMAIL=testuser100@yopmail.com REGISTRATION_USER_PASSWORD=Pass.1234 REGISTRATION_USER_PHONE=93749273 INVALID_EMAIL=invalidemail.com INVALID_PASSWORD=1 DIFFERENT_CONFIRM_PASSWORD=Pass.12345
And these variables in settings.py file.
REGISTRATION_USER_USERNAME = os.getenv("REGISTRATION_USER_USERNAME") REGISTRATION_USER_EMAIL = os.getenv("REGISTRATION_USER_EMAIL") REGISTRATION_USER_PASSWORD = os.getenv("REGISTRATION_USER_PASSWORD") REGISTRATION_USER_PHONE = os.getenv("REGISTRATION_USER_PHONE") INVALID_EMAIL = os.getenv("INVALID_EMAIL") INVALID_PASSWORD = os.getenv("INVALID_PASSWORD") DIFFERENT_CONFIRM_PASSWORD = os.getenv("DIFFERENT_CONFIRM_PASSWORD")
Adding these variables to settings our settings.py file will look like this
"""Application Settings""" import os from dotenv import load_dotenv load_dotenv() ADMIN_EMAIL = os.getenv("ADMIN_EMAIL") ADMIN_PASSWORD = os.getenv("ADMIN_PASSWORD") USER_EMAIL = os.getenv("USER_EMAIL") USER_PASSWORD = os.getenv("USER_PASSWORD") WRONG_EMAIL = os.getenv("WRONG_EMAIL") WRONG_PASSWORD = os.getenv("WRONG_PASSWORD") REGISTRATION_USER_USERNAME = os.getenv("REGISTRATION_USER_USERNAME") REGISTRATION_USER_EMAIL = os.getenv("REGISTRATION_USER_EMAIL") REGISTRATION_USER_PASSWORD = os.getenv("REGISTRATION_USER_PASSWORD") REGISTRATION_USER_PHONE = os.getenv("REGISTRATION_USER_PHONE") INVALID_EMAIL = os.getenv("INVALID_EMAIL") INVALID_PASSWORD = os.getenv("INVALID_PASSWORD") DIFFERENT_CONFIRM_PASSWORD = os.getenv("DIFFERENT_CONFIRM_PASSWORD")
So you get the idea of settings.py file right?
[ Step:02 ] Add Constants And URLs In Our Application
In our application/utils/urls.py file lets add these URLs.
REGISTRATION_URL = "https://querkiz.itech-theme.com/signup"
And in our application/utils/constants.py let’s add those constants of required XPath. I have said details about XPath in previous episodes.
REGISTRATION_USERNAME_FIELD_X_PATH = '/html/body/div/div/div/div/div/div/' 'div/form/div/div[1]/div/input[1]' REGISTRATION_EMAIL_FIELD_X_PATH = '/html/body/div/div/div/div/div/div/div/' 'form/div/div[2]/div/input' REGISTRATION_PASSWORD_FIELD_X_PATH = '/html/body/div/div/div/div/div/div/div/' 'form/div/div[3]/div/input' REGISTRATION_CONFIRM_PASSWORD_FIELD_X_PATH = '/html/body/div/div/div/div/' 'div/div/div/form/div/div[4]/' 'div/input' REGISTRATION_PHONE_FIELD_X_PATH = '/html/body/div/div/div/div/div/div/div/' 'form/div/div[5]/div/input' REGISTRATION_SUBMIT_BUTTON_X_PATH = '/html/body/div/div/div/div/div/div/div/' 'form/div/div[6]/div/button' REGISTRATION_VALIDATION_MESSAGE_BOX_X_PATHS = '//*[@id="notification_box"]/ul' REGISTRATION_SUCCESS_MESSAGE_BOX_X_PATHS = '//*[@id="notification_box"]' REGISTRATION_PAGE_X_PATHS = dict( username_field=REGISTRATION_USERNAME_FIELD_X_PATH, email_field=REGISTRATION_EMAIL_FIELD_X_PATH, password_field=REGISTRATION_PASSWORD_FIELD_X_PATH, confirm_password_field=REGISTRATION_CONFIRM_PASSWORD_FIELD_X_PATH, phone_field=REGISTRATION_PHONE_FIELD_X_PATH, submit_button=REGISTRATION_SUBMIT_BUTTON_X_PATH, error_message_box=REGISTRATION_VALIDATION_MESSAGE_BOX_X_PATHS, success_message_box=REGISTRATION_SUCCESS_MESSAGE_BOX_X_PATHS ) REGISTRATION_ERROR_MESSAGES = [ "Name field can not be empty", "Email field can not be empty", "Password field can not be empty", "Password confirmed field can not be empty", "The password confirmation does not match.", "Password length must be above 8 characters.", "Password must be consist of one Uppercase, one Lowercase and one Number!" ] REGISTRATION_SUCCESS_MESSAGE = "We have just sent a verification link on " "Email ." INVALID_EMAIL_MESSAGE = "Please enter an email address."
[ Step: 03 ] Developing The Registration Package
Nice! we are ready to go now. Write those below codes in our registration/registration.py file.
""" Registration module tasks: 1. Register with correct information 2. Register with wrong information 3. Check validation messages 4. Check success message 5. Test with invalid email """ from time import sleep from selenium import webdriver from selenium.common.exceptions import ElementNotInteractableException, NoSuchElementException, TimeoutException from application.utils.helpers import console_print class RegistrationTest: """RegistrationTest class""" def __init__(self, url: str, registration_x_paths: dict): self.url = url self.registration_x_paths = registration_x_paths self._browser = webdriver.Firefox() def visit_registration_page(self): """ Will visit the registration page """ try: self._browser.get(self.url) console_print('success', '[Registration page OK!]') sleep(2) except TimeoutException as error: console_print('failed', '[Registration page not OK!]') console_print('failed', str(error)) self._browser.quit() raise def register(self, username: str, email: str, password: str, confirm_password: str, phone: str): """ Invoke methods to set username, email, password, confirm password and submit the registration form :param phone: :param confirm_password: :param username: :param email: user email :param password: user password """ self.set_username(username) sleep(1) self.set_email(email) sleep(1) self.set_password(password) sleep(1) self.set_confirm_password(confirm_password) sleep(1) self.set_phone(phone) self.submit() sleep(2) def check_registration_success_message(self, username: str, email: str, password: str, confirm_password: str, phone: str, login_url: str) -> bool: try: self._browser.refresh() self.register(username, email, password, confirm_password, phone) sleep(2) current_url = self._browser.current_url if current_url is not login_url: console_print('failed', '[Current url is not login url, ' 'registration failed]') return False console_print('success', '[Current url is login url, ' 'registration successful]') return True except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', str(error)) self._browser.quit() return False def registration_with_invalid_email(self, username: str, email: str, password: str, confirm_password: str, phone: str, invalid_email_message): """ This method will try to register with a given invalid email where other information will be valid. And we will check the HTML validation message is shown or not. We will return True if it does otherwise, we will return False. :param username: :param email: :param password: :param confirm_password: :param phone: :param invalid_email_message: :return: """ try: self._browser.refresh() self.register(username, email, password, confirm_password, phone) sleep(1) email_validation_message = self._browser.find_element_by_xpath( self.registration_x_paths['email_field']) .get_attribute('validationMessage') sleep(1) print(email_validation_message) print(invalid_email_message) if email_validation_message != invalid_email_message: console_print('failed', '[Invalid email validation message ' 'didnt match!]') return False console_print('success', '[Invalid email validation message ' 'matched!]') return True except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', str(error)) self._browser.quit() return False def check_registration_validation_message(self, username: str, email: str, password: str, confirm_password: str, phone: str, error_messages: list) -> bool: """ Attempt a failed registration and then will check the validation messages is in our predefined expected list :param username: will be empty :param email: will be empty or invalid email :param password: invalid password will be given :param confirm_password: password and confirm password will not be same :param phone: might be empty for this particular test :param error_messages: predefined expected validation messages :return: True / False according to the test """ try: self._browser.refresh() sleep(1) self.register(username, email, password, confirm_password, phone) sleep(2) validation_message_ul = self._browser.find_element_by_xpath( self.registration_x_paths['error_message_box']) validation_messages_li = validation_message_ul. find_elements_by_tag_name('li') validation_messages = [] for li in validation_messages_li: validation_messages.append(li.text) print(validation_messages) for message in validation_messages: if message not in error_messages: console_print('failed', '[Validation message didnt match!]') print(message) return False console_print('success', '[All validation message matched!]') return True except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', str(error)) self._browser.quit() return False def set_username(self, username: str): """ Set username to the username field which will be selected by its XPath :param username: user email """ try: username_field = self._browser.find_element_by_xpath( self.registration_x_paths['username_field']) username_field.send_keys(username) except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', '[Username input failed!]') console_print('failed', str(error)) self._browser.quit() raise def set_email(self, email: str): """ Set email to the email field which will be selected by its XPath :param email: user email """ try: email_field = self._browser.find_element_by_xpath( self.registration_x_paths['email_field']) email_field.send_keys(email) except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', '[Email input failed!]') console_print('failed', str(error)) self._browser.quit() raise def set_password(self, password: str): """ Set password to the password field which will be selected by its XPath :param password: user password """ try: password_field = self._browser.find_element_by_xpath( self.registration_x_paths['password_field']) password_field.send_keys(password) except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', '[Password input failed!]') console_print('failed', str(error)) self._browser.quit() raise def set_confirm_password(self, confirm_password): """ Set same or wrong password to the confirm password field which will be selected by its XPath :param confirm_password: user password """ try: confirm_password_field = self._browser.find_element_by_xpath( self.registration_x_paths['confirm_password_field']) confirm_password_field.send_keys(confirm_password) except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', '[Confirm Password input failed!]') console_print('failed', str(error)) self._browser.quit() raise def set_phone(self, phone: str): """ Set phone to the phone field which will be selected by its XPath :param phone: user phone number """ try: phone_field = self._browser.find_element_by_xpath( self.registration_x_paths['phone_field']) phone_field.send_keys(phone) except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', '[Phone input failed!]') console_print('failed', str(error)) self._browser.quit() raise def submit(self): """ Get the submit button by its given XPath and click the button to submit the registration form """ try: submit_button = self._browser.find_element_by_xpath( self.registration_x_paths['submit_button']) submit_button.click() except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', '[Credential submit failed!]') console_print('failed', str(error)) self._browser.quit() print() raise
[ Step: 04 ] Code Breakdown & Explanation
Ow my God! Lots of code. But let’s breakdown. Slow and steady.
Initialize the attributes of RegistrationTest Class
In our __init__ method we are initializing URL, registration_x_paths. Also, we have initialized our web driver as _browser. Let’s break more,
self.url = url self.registration_x_paths = registration_x_paths self._browser = webdriver.Firefox()
This is how we will initiate our web driver. And we used the Firefox web driver in this episode. You need to install the driver in your system.
Visit registration page
Our visit_registration_page() method is responsible for visiting the given URL. We will hit the login page URL.
self._browser.get(self.url)
After that, we will print a colored message. And wait for 2 seconds.
console_print('success', '[Registration page OK!]') sleep(2)
But there could be an exception like a timeout. Last time we used the whole Exception class, which is really bad. We will use only what necessary here.
except TimeoutException as error: console_print('failed', '[Registration page not OK!]') console_print('failed', str(error)) self._browser.quit() raise
We handled the TimeoutException exception here and printed an error message and then raised the error.
Register method
The register() method will call the set_username(), set_email(), set_password(), set_confirm_password() and set_phone() method.
self.set_username(username) sleep(1) self.set_email(email) sleep(1) self.set_password(password) sleep(1) self.set_confirm_password(confirm_password) sleep(1) self.set_phone(phone) self.submit() sleep(2)
So, let’s break these methods.
The set_username() method will find the username field first by its XPath and will type the given username in it. ElementNotInteractableException, NoSuchElementException exceptions will be handled here as we are finding and interacting only.
def set_username(self, username: str): """ Set username to the username field which will be selected by its XPath :param username: user email """ try: username_field = self._browser.find_element_by_xpath( self.registration_x_paths['username_field']) username_field.send_keys(username) except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', '[Username input failed!]') console_print('failed', str(error)) self._browser.quit() raise
The same goes for other input field typing methods. The submit method will just find and click the submit button.
def submit(self): """ Get the submit button by its given XPath and click the button to submit the registration form """ try: submit_button = self._browser.find_element_by_xpath( self.registration_x_paths['submit_button']) submit_button.click() except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', '[Credential submit failed!]') console_print('failed', str(error)) self._browser.quit() print() raise
The registration_with_invalid_email() method
This method will try to register with a given invalid email where other information will be valid. And we will check the HTML validation message is shown or not. We will return True if it does otherwise, we will return False. Required parameters are
:param username: :param email: :param password: :param confirm_password: :param phone: :param invalid_email_message:
We will invoke the register() method first. And will wait for one second.
self.register(username, email, password, confirm_password, phone) sleep(1)
Now how can we get the HTML validation message that it will automatically show? When there is a required attribute? Here comes the get_attribute() method of the web driver.
email_validation_message = self._browser.find_element_by_xpath( self.registration_x_paths['email_field']) .get_attribute('validationMessage')
Check if the validation message we got from the web page and the given invalid_email_message parameter is the same or not. If it’s the same then we will return true.
if email_validation_message is not invalid_email_message: console_print('failed', '[Invalid email validation message ' 'didnt match!]') return False
If not, then call the developer and tell him to fix it. But before that, you better return false and get the hell out of your method.
console_print('failed', '[Invalid email validation message ' 'matched!]') print(email_validation_message) return True
The check_registration_validation_message() method
It’s time to disturb the fellow developer if he forgot to add backend validation. Attempt a failed registration and then will check the validation messages are in our predefined expected list or not. But first, refresh the browser to clear all the input fields and try to register.
self._browser.refresh() sleep(1) self.register(username, email, password, confirm_password, phone) sleep(2)
Fetch all the validation messages from the web page
validation_message_ul = self._browser.find_element_by_xpath( self.registration_x_paths['error_message_box']) validation_messages_li = validation_message_ul. find_elements_by_tag_name('li') validation_messages = [] for li in validation_messages_li: validation_messages.append(li.text)
Now check and ask your fellow developer to fix the validation if an unexpected thing happens.
for message in validation_messages: if message not in error_messages: console_print('failed', '[Validation message didnt match!]') print(message) return False console_print('success', '[All validation message matched!]') return True
The check_registration_success_message() method
We have tested on worst cases till now. Let’s see if it actually works. Call the register() method and fetch the success message from the web page.
self.register(username, email, password, confirm_password, phone) sleep(2) message = self._browser.find_element_by_xpath( self.registration_x_paths['success_message_box']).text
And now check if the current URL is LOGIN_URL or not.
current_url = self._browser.current_url if current_url is not login_url: console_print('failed', '[Current url is not login url, ' 'registration failed]') return False console_print('success', '[Current url is login url, ' 'registration successful]') return True
All set, now we are ready to write the execution code.
[ Step:05 ] It’s Time, Run The Automation
In our core package application, we will run the login automation testing. Write the below codes in the application/app.py file. We will run the homepage, login, and registration automation test synchronously.
"""Main application test execution area""" from homepage.homepage import HomepageTest from application.utils.urls import ( HOMEPAGE_URL, LOGIN_URL, ADMIN_DASHBOARD, REGISTRATION_URL) from login.login import LoginTest from registration.registration import RegistrationTest from application.utils.constants import ( HOMEPAGE_NAV_BAR, LOGIN_PAGE_X_PATHS, REGISTRATION_PAGE_X_PATHS, REGISTRATION_ERROR_MESSAGES, REGISTRATION_SUCCESS_MESSAGE, DASHBOARD_UPPER_RIGHT_MENU_X_PATH, LOGOUT_LINK_X_PATH, INVALID_EMAIL_MESSAGE ) from settings import ( ADMIN_EMAIL, ADMIN_PASSWORD, WRONG_EMAIL, WRONG_PASSWORD, REGISTRATION_USER_USERNAME, REGISTRATION_USER_EMAIL, REGISTRATION_USER_PASSWORD, REGISTRATION_USER_PHONE, INVALID_EMAIL, INVALID_PASSWORD, DIFFERENT_CONFIRM_PASSWORD ) if __name__ == '__main__': # Homepage Testing homepage = HomepageTest(HOMEPAGE_URL, HOMEPAGE_NAV_BAR) homepage.visit_homepage() homepage.nav_bar_content_testing() homepage.click_nav_elements_on_fullscreen() homepage.click_nav_elements_on_mobile_screen() homepage.close_browser() # Successful Login Testing login_test = LoginTest(LOGIN_URL, LOGIN_PAGE_X_PATHS, ADMIN_DASHBOARD) login_test.visit_login_page() login_test.login_attempt(ADMIN_EMAIL, ADMIN_PASSWORD) login_test.secondary_login_attempt_in_new_tab() login_test.logout(DASHBOARD_UPPER_RIGHT_MENU_X_PATH, LOGOUT_LINK_X_PATH) # Failed Login Testing login_test.visit_login_page() login_test.login_attempt(WRONG_EMAIL, WRONG_PASSWORD) # Registration Testing registration_test = RegistrationTest(REGISTRATION_URL, REGISTRATION_PAGE_X_PATHS) registration_test.visit_registration_page() # Failed registration testing without any values in form registration_test.check_registration_validation_message( '', '', '', '', '', REGISTRATION_ERROR_MESSAGES ) # Failed registration with invalid email registration_test.registration_with_invalid_email( REGISTRATION_USER_USERNAME, INVALID_EMAIL, REGISTRATION_USER_PASSWORD, REGISTRATION_USER_PASSWORD, REGISTRATION_USER_PHONE, INVALID_EMAIL_MESSAGE ) # Failed registration with invalid password registration_test.check_registration_validation_message( REGISTRATION_USER_USERNAME, REGISTRATION_USER_EMAIL, INVALID_PASSWORD, INVALID_PASSWORD, REGISTRATION_USER_PHONE, REGISTRATION_ERROR_MESSAGES ) # Failed registration with different password and confirm password registration_test.check_registration_validation_message( REGISTRATION_USER_USERNAME, REGISTRATION_USER_EMAIL, REGISTRATION_USER_PASSWORD, DIFFERENT_CONFIRM_PASSWORD, REGISTRATION_USER_PHONE, REGISTRATION_ERROR_MESSAGES ) # Successful registration test registration_test.check_registration_success_message( REGISTRATION_USER_USERNAME, REGISTRATION_USER_EMAIL, REGISTRATION_USER_PASSWORD, REGISTRATION_USER_PASSWORD, REGISTRATION_USER_PHONE, REGISTRATION_SUCCESS_MESSAGE )
Now run the test and enjoy the automation! Don’t forget to change the username and email. If everything works fine then you will find something similar like the below image.
And here is the video demo of the registration automation testing.
Now do one thing as practice. Try to register with an existing username or email and check it’s validation message. Keep practicing more. We will enter the core feature of the quiz application in the next episode. Till then, stay safe. Stay home and Don’t forget to share the article and your love for python. God bless you.
Github Repository: https://github.com/zim0101/quiz_app_automation_test_with_python_selenium
Web Automation with Selenium & Python Part 1