Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Using PyUnit for Testing a Selenium Python Test Suite

DZone's Guide to

Using PyUnit for Testing a Selenium Python Test Suite

Learn how to use the PyUnit framework for testing the Selenium test suite.

· Performance Zone ·
Free Resource

Sensu is an open source monitoring event pipeline. Try it today.

Selenium, as we know, is an open-source test suite used for cross-platform browser automation. Due to its popularity and ease of use, many compatible test frameworks with respect to different programming languages have been developed to extends support of Selenium for cross browser testing over multiple programming languages. Today, we are going to take a peek into one such testing framework, named PyUnit and which to run Selenium with Python.

PyUnit is a unit testing framework that is derived from JUnit for compatible execution of Selenium with Python language. PyUnit is a very popular testing framework to perform ‘Unit Testing’ – a mechanism to validate a particular piece of functionality in a module. The popularity of PyUnit as a Python specific unit testing framework has been a major reason for including PyUnit as an official Python module from version 2.5. The Python UnitTest library referred as unittest is widely used to perform automation testing using Selenium with Python.

Building Blocks of Python Unit Test [PyUnit] Framework

The unittest framework contains the following core-modules that are core to test case development and execution:

  • Test Loader: The Test Loader Python class is used to load test cases and test suites. It is also termed as 'Test Fixture.' Test Cases and Test Suite can be created locally or can be loaded from an external file/database. On successful execution, Test Suite object is released by the Test Loader and the same object can be used throughout the execution of the Test Case.
  • Test Runner: Responsible for displaying the output of the executed test to an end user using a runnable interface. The output could be represented in a variety of ways. It could be displayed as a standard code, in the form of GUI or through a textual representation.
  • Test Suite: Test Suite is a group of test cases that are ‘grouped logically’ based on the functionalities that are under test. Test suites are used for making the test code more modular and maintainable.
  • Test Case: Test Case contains the actual implementation of the test code. In case some functionality is not ready, a developer can make use of Stubs, i.e. dummy source code to test source code at a unit level.
  • Test Report: Test Reports are ideally used to organize the results of test case execution i.e. whether test case execution result is pass/fail. It also details the time taken for execution, execution summary, etc. Better the maintenance of test report, easier it extracts useful information from it.
  • TestCase class is used to create new tests and the FunctionTestCase acts as a subclass to TestCase class and make use of tests, which are appended to the existing unittest framework. setUp() and tearDown() are important components of the TestCase class that are used for initialization and clean up of the test fixture (that was created using setUp()). The primary purpose of using FunctionTestCase class is to aid the goal of code reusability.

PyUnit/unittest Overview

PyUnit also referred as unittest and works in a similar manner as xUnit which is a very popular unit testing framework whose structure and functionality is derived from Smalltalk’s SUnit. Hence, a developer who has prior experience with xUnit or other popular unit testing frameworks would find unittest easier to understand.

Testcase/Testsuite written using unittest module follows a common format

  1. Define a class derived from unittest.TestCase(). We discuss TestCase() in more detail in subsequent sections.
  2. Define testcases having the nomenclature test_name
  3. Execute the testcases/testsuites by having unittest.main(), which is normally placed at the bottom of the file i.e. after the necessary classes for testcase execution have been implemented.

We would have a look at the important classes in the unittest package in further sections.

setUp() and tearDown() — Initialization and De-Initialization

The setUp() method is an entry point to the test cases. It does not have any arguments. Since it is the entry point, some of the important initialization steps are performed in the implementation of the setUp() method. Below are some things related to initialization that can be included in setUp():

  • Load the test data
  • Creation of a browser instance e.g. Firefox using the WebDriver interface.
  • Opening files for I/O (Input/Output) operation, either for reading the test data or appending the results of the test execution.
import unittest
#Import other modules that are required for testing

class SearchText(unittest.TestCase):
def setUp(self):
#create a new FireFox session
self.driver = webdriver.FireFox()
.................................
.................................
self.driver.get("http://www.lambdatest.com")

def tearDown(self):
.................................
.................................
self.driver.quit()


In the above example, an instance of Firefox browser is created using the WebDriver API (Line 7). On successful creation, the Home Page http://www.lambdatest.com is opened on the Firefox browser. All the necessary operations which are required for unit testing are then performed. Once the unit test is complete, the cleanup is performed in the tearDown() API (Line 12). In a nutshell, setUp() and tearDown() are executed before and after each every test method.

Now that you have understood the basics of the initialization and de-initialization of a Unit test case written in Python, let’s have a look at some of the important classes in Python.

PyUnit – Classes and Functions

class unittest.TestCase(methodName=’TestName’)

The TestCase class is the base class where the infrastructure required for testing Selenium with Python is implemented. Specific test cases are implemented in subclasses. In many scenarios, no changes would be required in methodName nor runTest() needs to be implemented. We have already discussed the important initialization methods setUp() and tearDown(), now we would have a look at the other methods.

  •  setUpClass(): A class method is called before any of the individual tests are called. @classmethod is the identifier with which you can identify a  setUpClass()setUpClass() has only one argument, i.e. the class name.
  •  tearDownClass(): This method is called after all the tests in the class are executed. Similar to  setUpClass()tearDownClass() also as only one argument, i.e. the class name. The ‘class name’ should match with the name that is used in the setUpClass(); or else, it might result in an error. A sample implementation citing the usage of setUpClass() and tearDownClass() is shown below:  
import unittest

class Example(unittest.TestCase):
    @classmethod
    def setUpClass(class_name):
        print("setUpClass executed")

    def setUp(self):
        print("setUp executed")

    def test_1(self):
        print("test-1 executed")

    def test_2(self):
        print("test-2 executed")

    def tearDown(self):
        print("tearDown executed")

    @classmethod
    def tearDownClass(class_name):
        print("tearDownClass executed")


  • One question that could arise — What is the basic difference between setUp() and setUpClass() (or tearDown() and  tearDownClass())? setUp()  — and its de-initialization counterpart tearDown() — are loaded and executed before each test method, whereas setUpClass() — and its de-initialization counterpart tearDownClass()  — are executed once for the whole class. Let’s have a look at a simple example to understand the difference. As seen in the below example,  SetupClass-Python.py, depending on the test case being executed [test_1 or test_2], setUpClass gets executed once in the beginning and then the corresponding test case is executed. After the execution of the test case, the code in tearDownClass() is executed. In order to execute the code, press CTRL + F9. As seen in the snapshot below, both the test cases are executed to perform testing using Selenium with Python, and below is the sequence of execution:
    • Implementation under setUpClass() is executed
    •  test_1 is executed (setUp and  tearDown is called for test_1, i.e. initialization and cleanup after execution of  test_1)
    •  test_2 is executed ( setUp and tearDown is called for test_2 , i.e. initialization and cleanup after execution of test_2 )
    • Implementation under tearDownClass() is executed
    As explained earlier, setUpClass() and  tearDownClass() are executed only once, whereas setUp() and  tearDown() are executed for each test method.
  • run(result=None)TestResult object is passed as a parameter to the run() method. The parameter is optional and if the object is not supplied to the method; a temporary object is created and used to store the results. The result is passed to the caller of the run() method. We would discuss the TestResult object in detail in a subsequent section.

class unittest.TestSuite(tests=())

The TestSuite class is an aggregation of individual testcases and test suites. Instead of executing testcases on an iterative basis, developers and testers can make use of this class since makes the code maintenance easy.

Unlike the TestCase() class, the TestSuite() class does not contain any implementation of test code since it is used to group testcases on a logical/functional basis. Some of the methods that can be used along with TestSuite() class are mentioned below

  • addTest(test): It is used to add TestCase or TestSuite
  •  addTests(tests): In case you plan to iterate through the testcases or testsuites, you can make use of  addTests().
  • debug(): This method is primarily used for debugging purpose since it does not return the execution results.
  •  run(result): In case you plan to execute testcases associated with a testsuite, you can make use of  run(result). The result object stores the execution results.

class unittest.TestResult

 TestResult class is used to gather information about the number of test cases that have passed/failed/skipped. As discussed earlier, the TestCase and TestSuite classes are used to record the results of the testcase/testsuite execution. Some of the important attributes of TestResult are failures, errors, skipped, expectedFailuresunexpectedSuccesses .

Assertions In PyUnit Test Framework

Assertions are very popular in testing, irrespective of the programming language being used for test-code development. An assertion is nothing but a Boolean expression (representing ‘True/False’) which would carry a value ‘True’ till the time it encounters a bug/issue in the test code. Three types of assertions are available to check equivalence, comparison, and performing actions in case of exceptions.

Below are some of the popular assertions in PyUnit framework.

CHECKING MECHANISM SIGNIFINANCE SUMMARY PYTHON VERSION
[ONWARDS]
assertEqual(a, b [,msg]) a==b Checks whether ‘a’ equates ‘b’. msg is an optional parameter which can be used to pass a ‘message’ string
assertNotEqual(a,b[,msg]) a!=b Checks whether ‘a’ is not equal to ‘b’. msg is an optional parameter which can be used to pass a ‘message’ string
assertTrue(x[,msg]) bool(x) is True Verify if the given expression is True
assertFalse(x[,msg])) bool(x) is False Verify if the given expression is False
assertIsNot(a, b[,msg]) a is not b Checks whether a is not b 2.7
assertIs(a, b) a is b Checks whether a is b 2.7
assertIsNotNone(x[,msg]) x is not None Test whether expression x is Not None 2.7
assertIsNone(x[,msg]) x is None Test whether expression x is None 2.7
assertNotIn(x, y[,msg]) x is not in y Test whether ‘x’ is not a subset of ‘y’ 2.7
assertIn(x, y[,msg]) x is in y Test whether ‘x’ is a subset of ‘y’ 2.7
assertNotIsInstance(x, y[,msg]) x is not an instance of y Test whether ‘x’ is not an instance of ‘y’ 2.7
assertIsInstance(x, y[,msg]) x is an instance of y Test whether ‘x’ is an instance of ‘y’ 2.7

With the basics of PyUnit/unittest covered, let’s have a look at a sample code that can delve into the aspects we have discussed so far:

'''This sample program demonstrates the PyUnit/unittest framework'''   
# Inclusion of the PyUnit Test Framework              
import unittest    
def addition(x,y,z=0):   
# Add the three parameters which are passed to the addition function    
return x+y+z 

# This is where the test case is implemented 
class AddTest(unittest.TestCase):    
def setUp(self):       
# initialization code for the testcase/testsuite can be added here       
pass    
# These are tests that should be performed once the basic premise is set    
# Unit Tests start with test_    
# The addition method which was implemented earlier will be used here       
def test_addtion(self):       
self.assertEqual(addition(10,11,12), 33)       
# x=11, y=12, z=44 if (x+y)=z, the test would raise an assert since       
# the test is for assertNotEqual operation       
self.assertNotEqual(addition(11,12), 44)    
def test_negative_values(self):       
self.assertEqual(addition(-9,25), 16)       
self.assertNotEqual(addition(-9,25), 17)         
def tearDown(self):       
# Deinit and cleanup should be done here       
pass   
if __name__=='__main__':    
unittest.main()


The above code comprises of two testcases —  test_addition() and  test_negative_values(). A method named addition() [Line 6~ Line 8] is created to add three parameters that are passed to that method. assertNotEqual and assertEqual are used in the testcases. Respective testcases would pass if the conditions in the Assert equate to TRUE. When the above code is executed, the final test result is PASS as conditions mentioned in assertEqual and assertEqual are fulfilled, e.g. in test_addition()  testcase, assert would be invoked if addition of x=10,y=11,z=12 does not equate 33 since it is testing for ‘Equate’ operation. Below is the screenshot of the output when the code is compiled and tested from the command prompt [file – Python-unittest-output1.png]

PyUnit

In order to invoke an Assert, we make a small change in the test_negative_values()  testcase. Below is the modified source code [changes are marked as comment]

'''This sample program demonstrates the PyUnit/unittest framework'''   
# Inclusion of the PyUnit Test Framework              
import unittest    
def addition(x,y,z=0):    
# Add the three parameters which are passed to the addition function    
return x+y+z 
# This is where the test case is implemented class AddTest(unittest.TestCase):    
def setUp(self):       
# initialization code for the testcase/testsuite can be added here       
pass    
# These are tests that should be performed once the basic premise is set    
# Unit Tests start with test_    
# The addition method which was implemented earlier will be used here       
def test_addtion(self):       
self.assertEqual(addition(10,11,12), 33)       
# x=11, y=12, z=44 if (x+y)=z, the test would raise an assert since       
# the test is for assertNotEqual operation       
self.assertNotEqual(addition(11,12), 44)    
def test_negative_values(self):       
self.assertNotEqual(addition(-9,25), 16)   
#Changed Values#      
def tearDown(self):       
# Deinit and cleanup should be done here       
pass   
if __name__=='__main__':    
unittest.main()


As seen on Line-27, assert would be issued if (x+y)!=z. In our example, x=-9, y=25, and z=16. As the test condition fails, it issues assert. Below is the snapshot of the output

PyUnit

Summary

The above tutorial covered some of the important aspects of PyUnit, especially how this effective Unit testing framework can be used to automate cross browser testing code and how ‘asserts’ can be used effectively while testing your code. PyUnit can also be used to perform module-level testing since you also have the flexibility to execute corner test cases as well. During the course of the article, we used Notepad++ and ‘command line Python’ for writing PyUnit code and execution. As a developer, you should use the IDE and programming environment of your choice. Though PyUnit was written for ‘C’ Python, it is now possible to write PyUnit tests for languages like Java using the Jython framework. Covering Jython is beyond the scope of this article. You can find the PyUnit/unittest official documentation.

Sensu: workflow automation for monitoring. Learn more—download the whitepaper.

Topics:
selenium ,python framework ,selenium test automation ,selenium testing ,selenium - web browser automation ,selenium testing tutorial ,cross browser testing ,python tutorials

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}