Selenium Python Test Suite From Scratch Using Unittest Library
Selenium Python Test Suite From Scratch Using Unittest Library
Before starting with test automation, we should first prepare a set of test
cases for the features that are active in the Web application. These can be
cases meant for acceptance criteria or part of the functional testing
landscape.
Since not all these features are available in Selenium WebDriver, so we’ll
utilize the Python’s unit testing framework and use its features along with
Selenium Webdriver.
Along with this post, we would recommend you to read the below tutorial
as well. It’ll help you setup Selenium with Python and configure browsers
like Firefox, Chrome, and IE.
Let’s now look at the list of topics that we’re going to cover in this
Selenium Python tutorial.
Python Unittest library inherits its root from a third-party module known
as PyUnit. It was Steve Purcell who ideated PyUnit based on the famous
JUnit framework. And later it grew as an official Python module beginning
from version 2.5.
Like the JUnit, Python Unittest module splits up its functionality among
five key components. All five elements work in tandem to support
automation testing. Let’s discuss each of them one by one in detail.
Test Loader – It’s a Python class which loads test cases and suites created
locally or from an external data source like a file. It releases a TestSuite
object that carries those cases and suites.
Test Case – The TestCase class holds the test handlers and provides hooks
for preparing each handler and for cleaning up after execution.
Test Suite – It acts as a container for grouping test cases. With the help of
a test suite, you can combine a set of test cases representing specific
functionalities of the application under test.
We can create one or more tests by inheriting the TestCase class available
in the unittest module. To add a case, we also need to provide a
corresponding test method (a handler) to the derived class. To finalize a
test case, we can use assert or any of its variations for reporting test
status.
Here are some of the most common assert functions used almost in all
tests.
In addition to the test handler, we can also add routines like setup() and
tearDown() to manage the creation and disposition of any objects or
conditions that are mandatory for a test.
Let’s now start using the Unit test library and write a simple test by
inheriting the TestCase class. For this, you’ll need to import the <unittest>
module and define a class that inherits the TestCase class.
import unittest
from selenium import webdriver
class SearchText(unittest.TestCase):
A <setup()> method works as an entry point for the test cases. We can use
it to run a fixed set of actions before executing a test or all the tests
defined in the class.
These are pre-requisites which may include the following test setup
preparation tasks.
1. Create an instance of a browser driver.
2. Navigate to a base URL.
3. Load tests data for execution.
4. Open log files for recording inputs, statuses, and errors.
import unittest
from selenium import webdriver
class SearchText(unittest.TestCase):
def setUp(self):
# create a new Firefox session
self.driver = webdriver.Firefox()
self.driver.implicitly_wait(30)
self.driver.maximize_window()
# navigate to the application home page
self.driver.get("http://www.google.com/")
After creating a setup() method, we can now write some tests to verify the
application’s functionality. So, first of all, let’s define our use case.
Use Case – In this example, we will search for a text in google and
verify if the search returns a list of items
import unittest
from selenium import webdriver
class SearchText(unittest.TestCase):
def setUp(self):
# create a new Firefox session
self.driver = webdriver.Firefox()
self.driver.implicitly_wait(30)
self.driver.maximize_window()
# navigate to the application home page
self.driver.get("http://www.google.com/")
def test_search_by_text(self):
# get the search textbox
self.search_field = self.driver.find_element_by_name("q")
lists = self.driver.find_elements_by_class_name("r")
no=len(lists)
self.assertEqual(10, len(lists))
So to achieve this, the base TestCase class provides another method i.e.
tearDown() which the runner calls after test execution. It lets us clean the
values initialized at the beginning of the test via setup() method.
In our example, when the test execution ends, we no longer need the
instance of Firefox. So we will close it in the tearDown() method, as shown
in the following code.
import unittest
from selenium import webdriver
class SearchText(unittest.TestCase):
def setUp(self):
# create a new Firefox session
self.driver = webdriver.Firefox()
self.driver.implicitly_wait(30)
self.driver.maximize_window()
# navigate to the application home page
self.driver.get("http://www.google.com/")
def test_search_by_text(self):
# get the search textbox
self.search_field = self.driver.find_element_by_name("q")
lists = self.driver.find_elements_by_class_name("r")
no=len(lists)
self.assertEqual(11, len(lists))
def tearDown(self):
# close the browser window
self.driver.quit()
Running the tests from the command line would require us to add a call to
the main() method in the test script. We’ll also pass a verbosity argument
to the main(). It’ll get the test result details displayed on the console.
After adding these lines, save the test as a standard Python script and
name it as <selenium-python-test.py>. Then, try to execute it from the
command line by using the following command.
python selenium-python-test.py
After running the tests, the results would get displayed on the console
along with the executive summary as captured in the following screenshot.
So far, we’ve automated one simple test case. But we can add as many
cases as expected in the TestCase class. It’ll also help in creating logical
groups of tests that are related to specific functionality. So let’s add
another test to the TestCase class. Name the new method starting with the
word test, as shown in the following code.
def test_search_by_name(self):
# get the search textbox
self.search_field = self.driver.find_element_by_name("q")
# enter search keyword and submit
self.search_field.send_keys("Python class")
self.search_field.submit()
#get the list of elements which are displayed after the s
earch
#currently on result page using find_elements_by_class_na
me method
list_new = self.driver.find_elements_by_class_name("r")
self.assertEqual(10, len(list_new))
Executing the TestClass would result in the first opening and then closing
the two instances of Firefox. That’s how the setup() and tearDown()
methods work for each test method. You can tally the results from the
snapshot attached below.
Write Selenium Python Test Cases Using Unittest
In the previous examples, we were using the setup() method for creating
instances of Firefox driver. But this approach was leading to the creation
of a new instance of the web browser every time a new test case ran.
It was the setup() method which was causing this behavior as it runs
before every test case. The Same case is with the tearDown() method
which triggers for every test case after it finishes executing.
Let’s see the above example with modified code to call the setUpClass()
and tearDownClass() methods with the @classmethod decorator.
Selenium Python Test Script Example
import unittest
from selenium import webdriver
class SearchText(unittest.TestCase):
@classmethod
def setUpClass(inst):
# create a new Firefox session
inst.driver = webdriver.Firefox()
inst.driver.implicitly_wait(30)
inst.driver.maximize_window()
# navigate to the application home page
inst.driver.get("http://www.google.com/")
inst.driver.title
def test_search_by_text(self):
# get the search textbox
self.search_field = self.driver.find_element_by_name("q")
self.search_field.clear()
# enter search keyword and submit
self.search_field.send_keys("Selenium Webdriver interview
questions")
self.search_field.submit()
#get the list of elements which are displayed after the s
earch
#currently on result page using find_elements_by_class_na
me method
lists = self.driver.find_elements_by_class_name("r")
self.assertEqual(11, len(lists))
def test_search_by_name(self):
# get the search textbox
self.search_field = self.driver.find_element_by_name("q")
# enter search keyword and submit
self.search_field.send_keys("Python class")
self.search_field.submit()
#get the list of elements which are displayed after the s
earch
#currently on result page using find_elements_by_class_na
me method
list_new = self.driver.find_elements_by_class_name("r")
self.assertEqual(11, len(list_new))
@classmethod
def tearDownClass(inst):
# close the browser window
inst.driver.quit()
if __name__ == '__main__':
unittest.main()
Upon executing the test, we can see that both the tests are getting run in
the same Firefox browser.
The TestCase class of the Python Unittest library implements a list of assert
methods. We can use them to match actual values returned by the
application with the expected values. With every method, we can specify a
condition that must be true to continue executing the test.
1. Checking equivalence.
2. Logical comparison.
3. Acting in the case of Exceptions.
While running a test, the execution moves to the next line only if the given
assertion passes. Otherwise, the test would halt immediately prompting
with a failure message.
e.g. assertEqual(element.text,”10″)
assertTrue(x[,msg])) or assertFalse(x[,msg])) or assertIsNot(a,
b[,msg])) –
e.g. assertTrue(element.is_displayed())
assertListEqual(a, b) – This method checks whether the lists “a” and “b”
match. It helps to work with the drop-down fields.
fail() – This method fails the test unconditionally. It allows the creation of
custom conditional blocks.
Before we get into details of TestSuite, let’s add a new test to check the
home page of the application under test. We’ll aggregate this test along
with the previous search tests into a single test suite, as shown in the
following code.
class HomePageTest(unittest.TestCase):
@classmethod
def setUp(inst):
# create a new Firefox session """
inst.driver = webdriver.Firefox()
inst.driver.implicitly_wait(30)
inst.driver.maximize_window()
def test_search_box(self):
# check search box exists on Home page
self.assertTrue(self.is_element_present(By.NAME,"q"))
def test_language_settings(self):
# check language options on Home page
self.assertTrue(self.is_element_present(By.ID,"_eEe"))
def test_images_link(self):
# check images link on Home page
images_link = self.driver.find_element_by_link_text("Imag
es")
images_link.click()
# check search field exists on Images page
self.assertTrue(self.is_element_present(By.NAME,"q"))
self.search_field = self.driver.find_element_by_name("q")
# enter search keyword and submit
self.search_field.send_keys("Selenium Webdriver framework
architecture diagram")
self.search_field.submit()
@classmethod
def tearDown(inst):
# close the browser window
inst.driver.quit()
if __name__ == '__main__':
unittest.main(verbosity=2)
You would have now very well understood that we’ll use the TestSuite class
for defining and running the test suite. And we can add multiple test cases
into it. Also, apart from the TestSuite class, we need to use TestLoader and
TextTestRunner classes to create and run a test suite. Please refer the
below code.
import unittest
from SeleniumPythonRefactorTestCase import SearchText
from SeleniumPythonMultipleTests import HomePageTest
The TestLoader class reads all the test methods from the specified test files
that contain the definition of the test suite. Then, the TestRunner class take
control of the test suite and run all the tests specified. Below is the
command to run the new test suite script.
python SeleniumPythonTestSuite.py
It’ll run all the tests from the SearchText and HomePage classes and
generate the following output.
By default, the Python Unittest library emits the test output on the
terminal console. If you want to share the results with management and
stakeholders, then sending console logs isn’t the appropriate way.
import unittest
import HTMLTestRunner
import os
from SeleniumPythonRefactorTestCase import SearchText
from SeleniumPythonMultipleTests import HomePageTest
< https://cdn.techbeamers.com/wp-content/uploads/2016/10/Selenium-
Python-Test-Suite-With-HTML-Report.png>
You can now look at the attached HTML report and see that it’s presenting
all the required details of test execution. From the failure links, you can
even drill down the actual problem. Overall, it’s a nice looking report
which you can proudly share with all the stakeholders.
TechBeamers