Home Guide Start Selenium Testing with Python: Automated Testing of a User Signup Form

Start Selenium Testing with Python: Automated Testing of a User Signup Form

Vaibhav Singh, Full Stack Engineer At BrowserStack -

This blog demonstrates the use of web automation techniques using Selenium and Python on Google Chrome. This tutorial will help to automate the act of user signup on BrowserStack.

Before starting with the test case, it is necessary to get a sense of what Selenium is and how it works.

Introduction to Selenium

Selenium is a tool that allows developers to automate web browser activity with only a few lines of code across multiple platforms.nIn the software testing ecosystem, it allows users to use different web pages and simulate end-user behavior to a great extent.

Some things that can be accomplished with Selenium include, but are not limited to:

  • Clicking buttons
  • Performing clicks
  • Inputting text
  • Extracting text
  • Accessing Cookies
  • Pressing keys

Prerequisites – Initial Setup Process

Before the user starts writing code, the following steps are needed for setup of Selenium and Python:

Step #1 – Install Python 3.7

brew install python

Step #2 – Install Google Chrome

The Selenium module needs a webdriver to start playing with the browsers. Supported browsers are:

  • Chrome
  • Firefox
  • Internet Explorer
  • Safari
  • Opera
  • PhantomJS (invisible)

Step #3 – For Google Chrome, install ChromeDriver from brew or from here.

brew cask install chromedriver

Step #4 – Install the Selenium package using pip.

pip install selenium

Demo Test: How to open a webpage on Chrome Browser

Once the Selenium environment setup is complete, run a basic test using Selenium Python. Open your preferred text editor/IDE and type the following:

from selenium.webdriver import Chrome

browser = Chrome()
browser.get('https://www.browserstack.com')
print(browser.title)
browser.close()

This opens a Chrome browser, navigates to https://www.browserstack.com and extracts title using the methods available on our newly minted browser object. Now the user can query DOM using different methods defined in the browser object.

However, the question remains: How will the user know to query?

BrowserStack homepage with developer tools

Answer this by opening a web browser and using the developer tool to inspect the content on a web page. Let’s say that the user intends to search for the “Get started free” button on the BrowserStack home page and click on it so that it redirects to the next page. By inspecting the BrowserStack home page, one will see that the button has the id attribute signupModalButton. Add these lines after the get method call.

button = browser.find_element_by_id('signupModalButton')
button.click()

Now, everything seems to be working. The user can see the new page after clicking on the button.

BrowserStack user page

Learn to Automate Browser Related Actions

The above test has validated that browsers can be automated using Python. Now, one can automate the process of signing up as a new user and simulate user experience. To start, navigate to https://www.browserstack.com/users/sign_up and start exploring browser developer tools.

One can uniquely identify the name field by using ID user_full_name. Add these lines after button.click().

username = browser.find_element_by_id('user_full_name')
username.send_keys('John Doe')

The user should be able to see the name in the first field of the signup page.

Looking back at the browser, the user will see that each field can be uniquely identified using an ID. Now, they might want to fill all the details by repopulating their variables as they did a minute ago. However, this may require an additional step.

Some websites, including BrowserStack, have enabled ReCaptcha to identify bots and to avoid abuse on their platform. So when such scripts are used on their website, the user is recognized as a bot, which triggers ReCaptcha. This means that the user has to pass through an additional verification stage which involves identifying images of specific objects.

Unfortunately, there is no way around this verification layer, so this adds a delay of 1-2 seconds. Also, while filling data in the text field, enter characters with some delay instead of populating the text field in a second.

Create a function that will fill the text field with a character and add a delay of 0.3 seconds to mimic human behavior. Here, replace send_keys with slow_typing.

def slow_typing(element, text):
   for character in text: 
      element.send_keys(character)
      time.sleep(0.3)

Add a random pause to avoid being detected as a bot. Notably, in the Chrome browser, there is a default profile that will be used in this demonstration to mimic the behavior of a human who is visiting the website. The final script will look like this:

from selenium.webdriver import Chrome, ChromeOptions
import time

EMAIL_ID = "<your email ID>"

def slow_typing(element, text):
   for character in text:
      element.send_keys(character)
      time.sleep(0.3)

# Visit chrome://version/ and copy profile path in place of '<chrome user profile>'
options = ChromeOptions().add_argument("--user-data-dir=<chrome user profile>")

browser = Chrome(chrome_options=options)
browser.get('https://www.browserstack.com')

time.sleep(2)

# to accept cookie notification so that it doesn't interfere
cookie_cta = browser.find_element_by_id('accept-cookie-notification')
cookie_cta.click()

# Navigate to Signup Page
button = browser.find_element_by_id('signupModalButton')
button.click()

time.sleep(2)

# Fill user's full name
username = browser.find_element_by_id('user_fudll_name')
# username.send_keys('John Doe')
slow_typing(username, 'John Doe')

time.sleep(1)
# Fill user's email ID
email = browser.find_element_by_id('user_email_login')
slow_typing(email, EMAIL_ID)

time.sleep(2)
# Fill user's password
password = browser.find_element_by_id('user_password')

# Reads password from a text file because
# it's silly to save the password in a script.
with open('password.txt', 'r') as myfile:
       Password = myfile.read().replace('\n', '')
slow_typing(password, Password)

time.sleep(1)
# click on Terms and Conditions
toc = browser.find_element_by_name('terms_and_conditions')
toc.click()

# click on signup page
signupbutton = browser.find_element_by_id('user_submit')
signupbutton.click()

time.sleep(20)

browser.close()

Exploring the Signup Page

Now, that the signup process has been automated, explore the corner cases. Let’s say that if the user clicks on the signup button without filling data in the text field, they will receive an error message.

If the user wants to verify that all fields have the proper indication of error and appropriate error messages, they can use Selenium to automate the process.

Go back to the browser developer console, and look for how the error messages are populating as well as how the UI is changing.

BrowserStack UI with developer tools

Leave the signup form blank and click on the signup button. This shows a div class populated with the span with an error message in it. Also, all the text fields have a red border color, so use this color and messages to validate the form behavior.

To achieve this, find the CSS property and HTML content that has changed after the user clicks on the signup button. One can query any CSS property of an element using value_of_css_property method.

Below is the example code snippet to verify the border color of the text field:

username = browser.find_element_by_id('user_full_name')
if "error" in username.get_attribute('outerHTML'):
   obtained_color = username.value_of_css_property('border-bottom-color')
   if not check_color(obtained_color, "rgba(222, 20, 33, 1)"):
      print(f"expected color is {EXPECTED_COLOR} and got {obtained_color}")

Also, to extract content from the span, use the get_attribute method. First, find all elements which have class msg-body and then use the get_attribute method to extract the message in the span.

error_messages = ["At least 3 characters", "Invalid Email", "At least 6 characters"]
message_body_html_elements = browser.find_elements_by_class_name('msg-body')
for msg in message_body_html_elements:
   error_msg = msg.get_attribute('innerHTML').split("span")[1][1:-2]
   if error_msg not in error_messages:
      print(f"{msg.get_attribute('outerHTML')} is missing error message")

Now, add these validations for all the elements in the signup form, and convert it into a script. The final script is below:

from selenium.webdriver import Chrome, ChromeOptions
import time

EXPECTED_COLOR = "rgba(222, 20, 33, 1)"


# Visit chrome://version/ and copy profile path in place of '<chrome user profile>'
options = ChromeOptions().add_argument("--user-data-dir=<chrome user profile>")

browser = Chrome(chrome_options=options)
browser.get('https://www.browserstack.com')

# Navigate to Signup Page
button = browser.find_element_by_id('signupModalButton')
button.click()

time.sleep(4)

def check_color(color, orginal_color):
   return color == orginal_color

# click on signup page
signupbutton = browser.find_element_by_id('user_submit')
signupbutton.click()


username = browser.find_element_by_id('user_full_name')
if "error" in username.get_attribute('outerHTML'):
   obtained_color = username.value_of_css_property('border-bottom-color')
   if not check_color(obtained_color, "rgba(222, 20, 33, 1)"):
      print(f"expected color is {EXPECTED_COLOR} and got {obtained_color}")

email = browser.find_element_by_id('user_email_login')
if "error" in email.get_attribute('outerHTML'):
   obtained_color = email.value_of_css_property('border-bottom-color')
   if not check_color(obtained_color, "rgba(222, 20, 33, 1)"):
      print(f"expected color is {EXPECTED_COLOR} and got {obtained_color}")

password = browser.find_element_by_id('user_password')
if "error" in password.get_attribute('outerHTML'):
   obtained_color = password.value_of_css_property('border-bottom-color')
   if not check_color(obtained_color, "rgba(222, 20, 33, 1)"):
      print(f"expected color is {EXPECTED_COLOR} and got {obtained_color}")

error_messages = ["At least 3 characters", "Invalid Email", "At least 6 characters"]
message_body_html_elements = browser.find_elements_by_class_name('msg-body')
for msg in message_body_html_elements:
   error_msg = msg.get_attribute('innerHTML').split("span")[1][1:-2]
   if error_msg not in error_messages:
      print(f"{msg.get_attribute('outerHTML')} is missing error message")

browser.close()


How to build a class in Selenium Webdriver & Python

Since the same piece of code is being written repeatedly, it is wiser to build a class for this simple crawler. A class, in this case, does the following things:

  • Initializes browser and navigates to BrowserStack
  • Signs up as a user
  • Validates signup form

To know more about webdriver, read this Selenium Webdriver Tutorial.

Here’s the basic code in its entirety:

from selenium.webdriver import Chrome, ChromeOptions
import time

BASE_URL = "https://www.browserstack.com"
EMAIL_ID = "<your email ID>"
EXPECTED_COLOR = "rgba(222, 20, 33, 1)"


class BrowserstackCrawler(object):
   def __init__(self):
      # Visit chrome://version/ and copy profile path in place of '<chrome user profile>'
      options = ChromeOptions().add_argument("--user-data-dir=<chrome user profile>")
      self.browser = Chrome(chrome_options=options)
      self.browser.get(BASE_URL)

   def signup(self):
      cookie_cta = self.browser.find_element_by_id('accept-cookie-notification')
      cookie_cta.click()

      # Navigate to Signup Page
      button = self.browser.find_element_by_id('signupModalButton')
      button.click()

      time.sleep(2)

      # Fill user's full name
      username = self.browser.find_element_by_id('user_full_name')
      # username.send_keys('John Doe')
      self.slow_typing(username, 'John Doe')

      time.sleep(1)
      # Fill user's email ID
      email = self.browser.find_element_by_id('user_email_login')
      self.slow_typing(email, EMAIL_ID)

      time.sleep(2)
      # Fill user's password
      password = self.browser.find_element_by_id('user_password')

      # Reads password from a text file because
      # it's silly to save the password in a script.
      with open('password.txt', 'r') as myfile:
         Password = myfile.read().replace('\n', '')
      self.slow_typing(password, Password)

      time.sleep(1)
      # click on Terms and Condition
      toc = self.browser.find_element_by_name('terms_and_conditions')
      toc.click()

      # click on signup page
      signupbutton = self.browser.find_element_by_id('user_submit')
      signupbutton.click()

      # increase time so that you can manually pass Recaptcha
      # verification and confirm your email
      time.sleep(20)

      self.close_browser()

   def validate_signupform(self):
       cookie_cta = self.browser.find_element_by_id('accept-cookie-notification')
       cookie_cta.click()
       # Navigate to Signup Page
       button = self.browser.find_element_by_id('signupModalButton')
       button.click()

       time.sleep(4)

       # click on signup page
       signupbutton = self.browser.find_element_by_id('user_submit')
       signupbutton.click()


       username = self.browser.find_element_by_id('user_full_name')
       if "error" in username.get_attribute('outerHTML'):
          obtained_color = username.value_of_css_property('border-bottom-color')
          if not self.check_color(obtained_color, "rgba(222, 20, 33, 1)"):
             print(f"expected color is {EXPECTED_COLOR} and got {obtained_color}")

       email = self.browser.find_element_by_id('user_email_login')
       if "error" in email.get_attribute('outerHTML'):
          obtained_color = email.value_of_css_property('border-bottom-color')
          if not self.check_color(obtained_color, "rgba(222, 20, 33, 1)"):
             print(f"expected color is {EXPECTED_COLOR} and got {obtained_color}")

       password = self.browser.find_element_by_id('user_password')
       if "error" in password.get_attribute('outerHTML'):
          obtained_color = password.value_of_css_property('border-bottom-color')
          if not self.check_color(obtained_color, "rgba(222, 20, 33, 1)"):
             print(f"expected color is {EXPECTED_COLOR} and got {obtained_color}")

       error_messages = ["At least 3 characters",
                           "Invalid Email", "At least 6 characters"]
       message_body_html_elements = self.browser.find_elements_by_class_name('msg-body')
       for msg in message_body_html_elements:
          error_msg = msg.get_attribute('innerHTML').split("span")[1][1:-2]
          if error_msg not in error_messages:
             print(f"{msg.get_attribute('outerHTML')} is missing error message")

       self.close_browser()


   def slow_typing(self, element, text):
      for character in text:
         element.send_keys(character)
         time.sleep(0.3)


   def check_color(self, color, orginal_color):
      return color == orginal_color

   def close_browser(self):
      self.browser.close()


b1 = BrowserstackCrawler()
b1.signup()
b2 = BrowserstackCrawler()
b2.validate_signupform()

Conclusion

The experiment described above is only a glimpse of what one can do with Selenium. Combine it with Python, and it is possible to automate all actions on a web browser. Among other things, one can write Python scripts to create bots that send an alert when a bug is found on a website. Utilizing the full potential of Selenium and Python can go a long way in empowering developers and QAs to explore, automate, and review web browser functionality.

BrowserStack Logo Run Selenium Tests on 2000+ Browsers & Devices Get Started Free