So far we have done the basic home page automation testing. And authentication automation testing. Now we need to drive deep. Its time to test some core features of the quiz application. Let me introduce you to a feature called category in our quiz app. If we try to test this as old-time we had to do many forms submit and it hurts our finger. But we have selenium now. It will take care of our fingers. We can create a category and can create a subcategory under it. The category has these fields.
- Title [required]
- Parent Category
- Coin
- Question Limit [required]
- Quiz Limit [required]
- Time Limit [required]
- Serial [required]
- Status [required]
- Description
- Thumbnail Image
- White Image
So basically what we need to do is penetrate these fields with both wrong and right values. And today I’m going to give you an idea of how to do that. After that, you will be able to think more about testing.
The Automation Testing Application Structure
. ├── application │ ├── app.py │ ├── __init__.py │ ├── selenium_base.py │ └── utils │ ├── constants.py │ ├── helpers.py │ ├── __init__.py │ └── urls.py ├── category │ ├── constants.py │ ├── create.py │ ├── __init__.py │ └── test.py ├── homepage │ ├── homepage.py │ ├── __init__.py ├── login │ ├── __init__.py │ ├── login.py ├── README.md ├── registration │ ├── __init__.py │ └── registration.py ├── requirements.txt └── settings.py
Hold on. what is selenium_base.py doing here? Why we have a constants.py file in the category package? Guess what? We are going to make things a bit modular. What if we have another automation testing application. Where we have the same feature called category with the same functionality? Well, we just need to copy and paste the category package. That has been said. Now let’s penetrate the form.
[ Step:01 ] Add Constants And URLs In Our Application
Now let’s add the constants in category/constants.py file. The URLs and XPaths of fields and validation messages.
# urls CREATE_CATEGORY_URL = 'https://quiz.itech-theme.com/question-category-create' CATEGORY_LIST_URL = 'https://quiz.itech-theme.com/question-category-list' # constants # ---------------------------- Create Form XPath ------------------------------- TITLE_X_PATH = '/html/body/div[1]/div[2]/div[3]/div/div/div/div/div/form/' 'div[1]/div[1]/div/input' PARENT_CATEGORY_DROPDOWN_X_PATH = '/html/body/div[1]/div[2]/div[' '3]/div/div/div/div/div/form/div[1]/div[' '2]/div/div/select ' COIN_X_PATH = '/html/body/div[1]/div[2]/div[3]/div/' 'div/div/div/div/form/div[1]/div[3]/div/input' QUESTION_LIMIT_X_PATH = '/html/body/div[1]/div[2]/div[3]/div/' 'div/div/div/div/form/div[1]/div[4]/div/input' QUIZ_LIMIT_X_PATH = '/html/body/div[1]/div[2]/div[3]/div/div/' 'div/div/div/form/div[1]/div[5]/div/input' TIME_LIMIT_X_PATH = '/html/body/div[1]/div[2]/div[3]/div/' 'div/div/div/div/form/div[1]/div[6]/div/input' SERIAL_X_PATH = '/html/body/div[1]/div[2]/div[3]/div/div/' 'div/div/div/form/div[1]/div[7]/div/input' ACTIVATION_STATUS_DROPDOWN_X_PATH = '/html/body/div[1]/div[2]/div[3]/div/' 'div/div/div/div/form/div[1]/div[8]/' 'div/div/select' DESCRIPTION_X_PATH = '/html/body/div[1]/div[2]/div[3]/div/div/' 'div/div/div/form/div[1]/div[9]/div/textarea' THUMBNAIL_IMAGE_X_PATH = '//*[@id="input-file-now"]' WHITE_IMAGE_X_PATH = '//*[@id="input-file-now"]' SUBMIT_BUTTON_X_PATH = '/html/body/div[1]/div[2]/div[3]/div/' 'div/div/div/div/form/div[2]/div/button' # ------------------------------------------------------------------------------ PARENT_CATEGORY_OPTION_X_PATH = '/html/body/div[1]/div[2]/div[3]/div/div/' 'div/div/div/form/div[1]/div[2]/div/div/' 'select/option[53]' STATUS_ACTIVE_OPTION_X_PATH = '/html/body/div[1]/div[2]/div[3]/div/div/' 'div/div/div/form/div[1]/div[8]/div/' 'div/select/option[1]' SAMPLE_IMAGE_FILE_PATH = '/home/trex/Downloads/python_image.jpg' SUCCESS_VALIDATION_BOX_XPATH = '//*[@id="notification_box"]' CATEGORY_CREATION_SUCCESS_VALIDATION_MESSAGE = "Sub Category " "Created Successfully" CATEGORY_UPDATE_SUCCESS_VALIDATION_MESSAGE = "Sub Category Updated Successfully" TARGET_CATEGORY_X_PATH = '//*[@id="category-table"]/tbody/tr[21]/td[4]/a' TARGET_SUBCATEGORY_X_PATH = '//*[@id="category-table"]/tbody/tr[1]/td[9]/ul' '/a[1]' EDIT_FORM_HEADER_X_PATH = '/html/body/div[1]/div[2]/div[2]/div/div/div/div/h2' EDIT_FORM_HEADER_TEXT = 'Edit Sub Category' ERROR_VALIDATION_MESSAGES = [ "This name already taken", "Title field can not be empty", "Max limit field can not be empty", "Quiz limit field can not be empty", "Time limit field can not be empty", "Serial field can not be empty", ] # dictionaries CATEGORY_FORM_X_PATHS = dict( title=TITLE_X_PATH, parent_category=PARENT_CATEGORY_DROPDOWN_X_PATH, coin=COIN_X_PATH, question_limit=QUESTION_LIMIT_X_PATH, quiz_limit=QUIZ_LIMIT_X_PATH, time_limit=TIME_LIMIT_X_PATH, serial=SERIAL_X_PATH, status=ACTIVATION_STATUS_DROPDOWN_X_PATH, description=DESCRIPTION_X_PATH, thumbnail_image=THUMBNAIL_IMAGE_X_PATH, white_image=WHITE_IMAGE_X_PATH, submit=SUBMIT_BUTTON_X_PATH ) CATEGORY_FORM_DATA = dict( title='Python3.1.5', parent_category=PARENT_CATEGORY_OPTION_X_PATH, coin=100, question_limit=10, quiz_limit=10, time_limit=1, serial=243, status=STATUS_ACTIVE_OPTION_X_PATH, description='Programming category', thumbnail_image=SAMPLE_IMAGE_FILE_PATH, white_image=SAMPLE_IMAGE_FILE_PATH ) CATEGORY_FORM_EDIT_DATA = dict( title='Python2.4.3', parent_category=PARENT_CATEGORY_OPTION_X_PATH, coin=200, question_limit=20, quiz_limit=20, time_limit=2, serial=405, status=STATUS_ACTIVE_OPTION_X_PATH, description='Programming category', thumbnail_image=SAMPLE_IMAGE_FILE_PATH, white_image=SAMPLE_IMAGE_FILE_PATH ) ERROR_VALIDATION_MESSAGE_X_PATHS = dict( title='/html/body/div[1]/div[2]/div[3]/div/div/' 'div/div/div/form/div[1]/div[1]/div/span/strong', quistion_limit='/html/body/div[1]/div[2]/div[3]/div/div/' 'div/div/div/form/div[1]/div[4]/div/span/strong', quiz_limit='/html/body/div[1]/div[2]/div[3]/div/div/' 'div/div/div/form/div[1]/div[5]/div/span/strong', time_limit='/html/body/div[1]/div[2]/div[3]/div/div/' 'div/div/div/form/div[1]/div[6]/div/span/strong', serial='/html/body/div[1]/div[2]/div[3]/div/div/' 'div/div/div/form/div[1]/div[7]/div/span/strong' ) DUPLICATE_NAME_VALIDATION_MESSAGE_X_PATH = '/html/body/div[1]/div[2]/div[3]/' 'div/div/div/div/div/form/div[1]/' 'div[1]/div/span/strong'
[ Step: 02 ] Refactoring
SeleniumBase Class
We will demonstrate the creation feature today. but to create we need to login first right? And we also need to stay in the same browser. Well, Let’s write a base class for selenium.
Let’s import the selenium modules
"""Base class for selenium operation""" from selenium import webdriver from selenium.common.exceptions import WebDriverException
Now let’s write the class and its __init__ method.
class SeleniumBase: """Base class for selenium driver""" def __init__(self, browser=None): """ :param browser: for cain testing """ if browser is None: self.browser = webdriver.Firefox() else: self.browser = browser
Now let’s write the most repeated methods, visit URL and closing browser.
def visit_url(self, url: str): """ Visit a specific url :param url: """ try: self.browser.get(url) except WebDriverException: self.close_browser() raise def close_browser(self): """ Quit this browser instance """ self.browser.quit()
After that, your SeleniumBase class will look like this.
"""Base class for selenium operation""" from selenium import webdriver from selenium.common.exceptions import WebDriverException class SeleniumBase: """Base class for selenium driver""" def __init__(self, browser=None): """ :param browser: for cain testing """ if browser is None: self.browser = webdriver.Firefox() else: self.browser = browser def visit_url(self, url: str): """ Visit a specific url :param url: """ try: self.browser.get(url) except WebDriverException: self.close_browser() raise def close_browser(self): """ Quit this browser instance """ self.browser.quit()
Login Class Refactor
Now let’s refactor our login class. As we build a base class for selenium operation. We need to inherit the SeleniumBase class.
class LoginTest(SeleniumBase): """ LoginTest class test successful and failed login attempt as well as try to login after a successful login in a new tab. Log out testing can also be done. """ def __init__(self, url: str, login_page_x_paths: dict, dashboard_url: str): """ :param url: Web application's login url :param login_page_x_paths: form and button XPaths :param dashboard_url: dashboard url after login redirection """ super().__init__() self.url = url self.login_page_x_paths = login_page_x_paths self.dashboard_url = dashboard_url
[ Step: 03 ] Developing The Category Package
CategoryCreateTest Class
We have created a file called “create.py”. Where we will develop the creation testing of a category/subcategory. Let’s import all the things we need first.
"""Category Create""" from time import sleep from selenium.common.exceptions import ( ElementNotInteractableException, NoSuchElementException, JavascriptException) from application.selenium_base import SeleniumBase import unittest from application.utils.helpers import console_print from category.constants import (CATEGORY_FORM_X_PATHS, SUCCESS_VALIDATION_BOX_XPATH, ERROR_VALIDATION_MESSAGE_X_PATHS, ERROR_VALIDATION_MESSAGES, DUPLICATE_NAME_VALIDATION_MESSAGE_X_PATH)
Now let’s create the class by extending SeleniumBase class and TestCase class from “unittest”. Guess what? We are implementing unit testing which we will cover in another episode.
class CategoryCreateTest(SeleniumBase, unittest.TestCase): def __init__(self, browser=None): """ :param browser: chained previous browser """ super().__init__(browser) if browser is not None: self.browser = browser
Set Title Method
Now to submit a form we need to set all the field right? No problem, let’s go step by step. Set the title first with this method below. It will take a parameter called title which is a string. We will find the title field by XPath with the help of the find_element_by_xpath() method. And we will set the title with the send_keys() method.
def set_title(self, title: str): """ Set title :param title: category title """ try: title_field = self.browser.find_element_by_xpath( CATEGORY_FORM_X_PATHS['title']) title_field.send_keys(title) console_print('success', '[Title has been set]') self.assertTrue(True) except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', str(error)) self.close_browser() raise
Set the parent category method
In order to select a parent category, we need to select a drop-down option. We will find it by it’s XPath. And we will trigger a click event with click() method and will click on an option.
def set_parent_category(self, option_xpath: str): """ Set parent category :param option_xpath: option xpath """ try: parent_category_field = self.browser.find_element_by_xpath( CATEGORY_FORM_X_PATHS['parent_category']) parent_category_field.click() option = self.browser.find_element_by_xpath(option_xpath) option.click() console_print('success', '[Parent category has been set]') self.assertTrue(True) except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', str(error)) self.close_browser() raise
Set coin method
Same as setting the title. It will also take a parameter called coin and its an integer. We will use the send_keys() method to set the coin value.
def set_coin(self, coin: int): """ Set coin :param coin: category coin """ try: coin_field = self.browser.find_element_by_xpath( CATEGORY_FORM_X_PATHS['coin']) coin_field.send_keys(coin) console_print('success', '[Coin has been set]') self.assertTrue(True) except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', str(error)) self.close_browser() raise
Set other fields
So, just like the other methods, we will set all the fields except the file fields.
def set_question_limit(self, question_limit: str): """ Set question_limit :param question_limit: category question_limit """ try: question_limit_field = self.browser.find_element_by_xpath( CATEGORY_FORM_X_PATHS['question_limit']) question_limit_field.send_keys(question_limit) console_print('success', '[Question limit has been set]') self.assertTrue(True) except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', str(error)) self.close_browser() raise def set_quiz_limit(self, quiz_limit: str): """ Set quiz_limit :param quiz_limit: category quiz_limit """ try: quiz_limit_field = self.browser.find_element_by_xpath( CATEGORY_FORM_X_PATHS['quiz_limit']) quiz_limit_field.send_keys(quiz_limit) console_print('success', '[Quiz limit has been set]') self.assertTrue(True) except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', str(error)) self.close_browser() raise def set_time_limit(self, time_limit: str): """ Set time_limit :param time_limit: category time_limit """ try: time_limit_field = self.browser.find_element_by_xpath( CATEGORY_FORM_X_PATHS['time_limit']) time_limit_field.send_keys(time_limit) console_print('success', '[Time limit has been set]') self.assertTrue(True) except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', str(error)) self.close_browser() raise def set_serial(self, serial: str): """ Set serial :param serial: category serial """ try: serial_field = self.browser.find_element_by_xpath( CATEGORY_FORM_X_PATHS['serial']) serial_field.send_keys(serial) console_print('success', '[Serial has been set]') self.assertTrue(True) except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', str(error)) self.close_browser() raise def set_status(self, option_xpath: str): """ Set status :param option_xpath: option xpath """ try: status_field = self.browser.find_element_by_xpath( CATEGORY_FORM_X_PATHS['status']) status_field.click() option = self.browser.find_element_by_xpath(option_xpath) option.click() console_print('success', '[Status has been set]') self.assertTrue(True) except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', str(error)) self.close_browser() raise def set_description(self, description: str): """ Set description :param description: category description """ try: description_field = self.browser.find_element_by_xpath( CATEGORY_FORM_X_PATHS['description']) description_field.send_keys(description) console_print('success', '[Serial has been set]') self.assertTrue(True) except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', str(error)) console_print('failed', str(error)) self.close_browser() raise
Select Image File
All right, now to set an image file to a field that you need to do is, set a constant for a file path from your local machine. And submit it to the file fields.
def set_thumbnail_image(self, thumbnail_image_path: str): """ Upload thumbnail image for category :param thumbnail_image_path: given image path in local machine """ try: thumbnail_image_field = self.browser.find_element_by_xpath( CATEGORY_FORM_X_PATHS['thumbnail_image']) thumbnail_image_field.send_keys(thumbnail_image_path) console_print('success', '[Thumbnail image has been set]') self.assertTrue(True) except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', str(error)) self.close_browser() raise def set_white_image(self, white_image_path: str): """ Upload white image for category :param white_image_path: given image path in local machine """ try: white_image_field = self.browser.find_element_by_xpath( CATEGORY_FORM_X_PATHS['white_image']) white_image_field.send_keys(white_image_path) console_print('success', '[White image has been set]') self.assertTrue(True) except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', str(error)) self.close_browser() raise
Submit method
Now all methods have been written for filling the fields. We need to write a method to submit the form.
def submit(self): """ Submit category creation form """ try: submit_button = self.browser.find_element_by_xpath( CATEGORY_FORM_X_PATHS['submit']) submit_button.click() console_print('success', '[Submit button clicked]') self.assertTrue(True) sleep(1) except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', str(error)) self.close_browser() raise
Create Category Method
Now to create the category we need to invoke all the methods one after another in our category method, which will take a parameter called form_data. And its a dictionary.
def create(self, form_data: dict): """ Set all input fields by invoking their methods and submit the create form :param form_data: given form data as dictionary from constant file """ try: self.set_title(form_data['title']) self.set_parent_category(form_data['parent_category']) self.set_coin(form_data['coin']) self.set_question_limit(form_data['question_limit']) self.set_quiz_limit(form_data['quiz_limit']) self.set_time_limit(form_data['time_limit']) self.set_serial(form_data['serial']) self.set_status(form_data['status']) self.set_description(form_data['description']) self.set_thumbnail_image(form_data['thumbnail_image']) self.set_white_image(form_data['white_image']) self.submit() console_print('success', '[Form has been submitted]') self.assertTrue(True) except Exception as error: console_print('failed', str(error)) raise
Success message checking method
Now how can we actually check if the category has been created? By the success message of course. This method will take a parameter called message and will compare the message with the web page success message.
def success_validation_message_is_ok(self, message): """ Check the validation message of a successful category creation :param message: success validation message """ try: message_box = self.browser.find_element_by_xpath( SUCCESS_VALIDATION_BOX_XPATH) inner_text = message_box.text clean_text = inner_text.replace('x', '') print(len(clean_text.strip())) print(len(message.strip())) if clean_text.strip() == message.strip(): console_print('success', '[Success validation message matched]') self.assertTrue(True) else: console_print('failed', '[Success validation didnt' ' message match]') except ( ElementNotInteractableException, NoSuchElementException, JavascriptException) as error: console_print('failed', str(error)) raise
Method to test error validation message
We have all the validation messages as an array in our constants.py. We need to grab all the messages from the web page and will check if any of the messages are not in our predefined message list.
def error_validation_message_is_ok(self): """ Check all error validation message by giving empty string to all fields """ try: for message_x_path in ERROR_VALIDATION_MESSAGE_X_PATHS: message = self.browser.find_element_by_xpath( ERROR_VALIDATION_MESSAGE_X_PATHS[message_x_path]).text if message not in ERROR_VALIDATION_MESSAGES: console_print('failed', '[' + message_x_path + 'validation message didnt match!]') print(message) else: console_print('success', '[All validation message matched!]') except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', str(error)) raise
Method to test duplicate category creation
We can’t create a category by an existing name. We need to check that horrible thing. How can we do that? Let’s try to create a category with the existing name and let’s check the message under the title field.
def duplicate_category_creation(self, form_data): """ Submit form with a duplicate category name :param form_data: """ try: self.create(form_data) message = self.browser.find_element_by_xpath( DUPLICATE_NAME_VALIDATION_MESSAGE_X_PATH) if message not in ERROR_VALIDATION_MESSAGES: console_print('success', '[Duplicate name ' 'validation message didnt match!]') else: console_print('failed', '[Duplicate name ' 'validation message matched!]') except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', str(error)) raise
After adding all the methods, your create.py file will look like this.
"""Category Create""" from time import sleep from selenium.common.exceptions import ( ElementNotInteractableException, NoSuchElementException, JavascriptException) from application.selenium_base import SeleniumBase import unittest from application.utils.helpers import console_print from category.constants import (CATEGORY_FORM_X_PATHS, SUCCESS_VALIDATION_BOX_XPATH, ERROR_VALIDATION_MESSAGE_X_PATHS, ERROR_VALIDATION_MESSAGES, DUPLICATE_NAME_VALIDATION_MESSAGE_X_PATH) class CategoryCreateTest(SeleniumBase, unittest.TestCase): def __init__(self, browser=None): """ :param browser: chained previous browser """ super().__init__(browser) if browser is not None: self.browser = browser def set_title(self, title: str): """ Set title :param title: category title """ try: title_field = self.browser.find_element_by_xpath( CATEGORY_FORM_X_PATHS['title']) title_field.send_keys(title) console_print('success', '[Title has been set]') self.assertTrue(True) except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', str(error)) self.close_browser() raise def set_parent_category(self, option_xpath: str): """ Set parent category :param option_xpath: option xpath """ try: parent_category_field = self.browser.find_element_by_xpath( CATEGORY_FORM_X_PATHS['parent_category']) parent_category_field.click() option = self.browser.find_element_by_xpath(option_xpath) option.click() console_print('success', '[Parent category has been set]') self.assertTrue(True) except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', str(error)) self.close_browser() raise def set_coin(self, coin: str): """ Set coin :param coin: category coin """ try: coin_field = self.browser.find_element_by_xpath( CATEGORY_FORM_X_PATHS['coin']) coin_field.send_keys(coin) console_print('success', '[Coin has been set]') self.assertTrue(True) except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', str(error)) self.close_browser() raise def set_question_limit(self, question_limit: str): """ Set question_limit :param question_limit: category question_limit """ try: question_limit_field = self.browser.find_element_by_xpath( CATEGORY_FORM_X_PATHS['question_limit']) question_limit_field.send_keys(question_limit) console_print('success', '[Question limit has been set]') self.assertTrue(True) except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', str(error)) self.close_browser() raise def set_quiz_limit(self, quiz_limit: str): """ Set quiz_limit :param quiz_limit: category quiz_limit """ try: quiz_limit_field = self.browser.find_element_by_xpath( CATEGORY_FORM_X_PATHS['quiz_limit']) quiz_limit_field.send_keys(quiz_limit) console_print('success', '[Quiz limit has been set]') self.assertTrue(True) except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', str(error)) self.close_browser() raise def set_time_limit(self, time_limit: str): """ Set time_limit :param time_limit: category time_limit """ try: time_limit_field = self.browser.find_element_by_xpath( CATEGORY_FORM_X_PATHS['time_limit']) time_limit_field.send_keys(time_limit) console_print('success', '[Time limit has been set]') self.assertTrue(True) except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', str(error)) self.close_browser() raise def set_serial(self, serial: str): """ Set serial :param serial: category serial """ try: serial_field = self.browser.find_element_by_xpath( CATEGORY_FORM_X_PATHS['serial']) serial_field.send_keys(serial) console_print('success', '[Serial has been set]') self.assertTrue(True) except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', str(error)) self.close_browser() raise def set_status(self, option_xpath: str): """ Set status :param option_xpath: option xpath """ try: status_field = self.browser.find_element_by_xpath( CATEGORY_FORM_X_PATHS['status']) status_field.click() option = self.browser.find_element_by_xpath(option_xpath) option.click() console_print('success', '[Status has been set]') self.assertTrue(True) except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', str(error)) self.close_browser() raise def set_description(self, description: str): """ Set description :param description: category description """ try: description_field = self.browser.find_element_by_xpath( CATEGORY_FORM_X_PATHS['description']) description_field.send_keys(description) console_print('success', '[Serial has been set]') self.assertTrue(True) except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', str(error)) console_print('failed', str(error)) self.close_browser() raise def set_thumbnail_image(self, thumbnail_image_path: str): """ Upload thumbnail image for category :param thumbnail_image_path: given image path in local machine """ try: thumbnail_image_field = self.browser.find_element_by_xpath( CATEGORY_FORM_X_PATHS['thumbnail_image']) thumbnail_image_field.send_keys(thumbnail_image_path) console_print('success', '[Thumbnail image has been set]') self.assertTrue(True) except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', str(error)) self.close_browser() raise def set_white_image(self, white_image_path: str): """ Upload white image for category :param white_image_path: given image path in local machine """ try: white_image_field = self.browser.find_element_by_xpath( CATEGORY_FORM_X_PATHS['white_image']) white_image_field.send_keys(white_image_path) console_print('success', '[White image has been set]') self.assertTrue(True) except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', str(error)) self.close_browser() raise def submit(self): """ Submit category creation form """ try: submit_button = self.browser.find_element_by_xpath( CATEGORY_FORM_X_PATHS['submit']) submit_button.click() console_print('success', '[Submit button clicked]') self.assertTrue(True) sleep(1) except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', str(error)) self.close_browser() raise def create(self, form_data: dict): """ Set all input fields by invoking their methods and submit the create form :param form_data: given form data as dictionary from constant file """ try: self.set_title(form_data['title']) self.set_parent_category(form_data['parent_category']) self.set_coin(form_data['coin']) self.set_question_limit(form_data['question_limit']) self.set_quiz_limit(form_data['quiz_limit']) self.set_time_limit(form_data['time_limit']) self.set_serial(form_data['serial']) self.set_status(form_data['status']) self.set_description(form_data['description']) self.set_thumbnail_image(form_data['thumbnail_image']) self.set_white_image(form_data['white_image']) self.submit() console_print('success', '[Form has been submitted]') self.assertTrue(True) except Exception as error: console_print('failed', str(error)) raise def success_validation_message_is_ok(self, message): """ Check the validation message of a successful category creation :param message: success validation message """ try: message_box = self.browser.find_element_by_xpath( SUCCESS_VALIDATION_BOX_XPATH) inner_text = message_box.text clean_text = inner_text.replace('x', '') print(len(clean_text.strip())) print(len(message.strip())) if clean_text.strip() == message.strip(): console_print('success', '[Success validation message matched]') self.assertTrue(True) else: console_print('failed', '[Success validation didnt' ' message match]') except ( ElementNotInteractableException, NoSuchElementException, JavascriptException) as error: console_print('failed', str(error)) raise def error_validation_message_is_ok(self): """ Check all error validation message by giving empty string to all fields """ try: for message_x_path in ERROR_VALIDATION_MESSAGE_X_PATHS: message = self.browser.find_element_by_xpath( ERROR_VALIDATION_MESSAGE_X_PATHS[message_x_path]).text if message not in ERROR_VALIDATION_MESSAGES: console_print('failed', '[' + message_x_path + 'validation message didnt match!]') print(message) else: console_print('success', '[All validation message matched!]') except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', str(error)) raise def duplicate_category_creation(self, form_data): """ Submit form with a duplicate category name :param form_data: """ try: self.create(form_data) message = self.browser.find_element_by_xpath( DUPLICATE_NAME_VALIDATION_MESSAGE_X_PATH) if message not in ERROR_VALIDATION_MESSAGES: console_print('success', '[Duplicate name ' 'validation message didnt match!]') else: console_print('failed', '[Duplicate name ' 'validation message matched!]') except ( ElementNotInteractableException, NoSuchElementException) as error: console_print('failed', str(error)) raise
Now all the methods have been written. Let’s write the test code to execute them.
[ Step: 04 ] Test Category Creation
In our test.py file import the necessary things.
"""Category automation testing unit""" from application.utils.urls import (LOGIN_URL, ADMIN_DASHBOARD) from category.edit import CategoryEditTest from login.login import LoginTest from category.create import CategoryCreateTest from application.utils.constants import LOGIN_PAGE_X_PATHS from settings import ( ADMIN_EMAIL, ADMIN_PASSWORD ) from category.constants import *
Now let’s try to login first
if __name__ == '__main__': # Successful Login login_test = LoginTest(LOGIN_URL, LOGIN_PAGE_X_PATHS, ADMIN_DASHBOARD) login_test.visit_url(LOGIN_URL) login_test.login_attempt(ADMIN_EMAIL, ADMIN_PASSWORD)
Now we will try to create a category with the right credentials using the browser property from Login class
# Category Successful create Test category_create_test = CategoryCreateTest(login_test.browser) category_create_test.visit_url(CREATE_CATEGORY_URL) category_create_test.create(CATEGORY_FORM_DATA) category_create_test.success_validation_message_is_ok( CATEGORY_CREATION_SUCCESS_VALIDATION_MESSAGE)
Now We will try to create a category with wrong inputs. Also, we will check the error message for that.
# Category create validation message Test category_create_test.visit_url(CREATE_CATEGORY_URL) category_create_test.submit() category_create_test.error_validation_message_is_ok()
Well, all we have to do is the error validation message testing.
# Try to save with duplicate name category_create_test.duplicate_category_creation(CATEGORY_FORM_DATA)
Congratulation! After running the test.py file we will have a result similar result like the demo below.
In our next episode, we will do the category update testing. Don’t forget to share with your friends who are recently learning web automation. Here are the GitHub link and previous episodes.
Github Repository: Web Automation Of Quiz Application
Previous Episodes:
Web Automation with Selenium & Python Part 1 [Setup & Installation]
Web Automation with Selenium & Python Part 2 [Homepage Automation]
Web Automation with Selenium & Python Part 3 [Login Automation]
Web Automation with Selenium & Python Part 4 [Registration Automation]