Unit testing in Python

Unit testing

Unit testing is an approach to test the source code as individual units and to determine whether they are working properly.Unit testing is mainly done by program developers to make sure that the code is fit to use.

There are several advantages for unit testing.

First of all we can quote the obvious reason that it helps to identify problems early.
Unit testing provides another advantage that it will allow us to modify the program easily. That means if we modify the program then we can run unit testing again on the remaining parts to check whether they work properly.
Unit testing makes Integration testing easier. Another advantage is that unit testing files will act as documentation. We can easily understand the functionality of the program by referring these files.


Unit testing in Python

In object oriented programming a testing unit may be a class or a method. Even though python ain't  full object oriented Unit testing follows the object oriented align.

From Python 2.1 we can find the module unittest for Unit testing.
Normally we write the testing code before we write our program code.

Here i am trying to shed some light on unit testing by using a small sqrteg.py program.
The program is simple.It contains two functions named as sqIt (means square It) for finding the square and sqrtIt (meaning square root It) to find the square root of a given number.


Lets start by creating a unit test file sqrtegtest.py for this sqrteg.py program before we write any code in it.

First of all i provided some test data in sqrtegtest.py using which the test file checks the conditions.


 class TestData(unittest.TestCase):  
      TestData=( (0,0),  
           (1,1),  
             (2,4),  
           (3,9),  
           (8.5,72.25),  
           (10,100),  
           (55.55,3085.8025),  
           (100,10000) )  

Then we create two methods to check the working of known input with known result.


      def testSqItTestData(self):  
           """sqIt give known result with known output"""  
           for integer,square in self.TestData:  
                result=sqrteg.sqIt(integer)  
                self.assertEqual(square, result)  
      def testSqrtItTestData(self):  
           """sqrtIt give known result with known output"""  
           for integer,square in self.TestData:  
                result=sqrteg.sqrtIt(square)  
                self.assertEqual(integer, result)  

Then we focus on the bad inputs that can be given to the sqIt() and sqrtIt() functions (still not created).

Lets say string input is bad to these programs.So we have to focus on the fact that these methods take only int or float types.
Also assume that sqrtIt (square root method) takes no negative numbers(Just to simplify it from complex numbers).


 class SqItBadInput(unittest.TestCase):  
      def testSqItInput(self):  
           """sqIt fails for bad input"""  
           self.assertRaises(sqrteg.TypeError, sqrteg.sqrtIt, 'hello')  
 class SqrtItBadInput(unittest.TestCase):  
      def testSqrtItInput(self):  
           """sqrtIt fails for bad input"""  
           self.assertRaises(sqrteg.TypeError, sqrteg.sqrtIt, 'hello')  
      def testNegative(self):  
           self.assertRaises(sqrteg.OutOfRangeError, sqrteg.sqrtIt, -1)  

We pass a sample string 'hello' (of string type) as a bad input. Also for sqrtIt() we pass -1 (a negative number) as OutOfRangeError.

Last but not least we do the sanity check.The following code itself explains what it means.



 class SanityCheck(unittest.TestCase):   
      def testSanity(self):   
           """sqrtIt(sqIt(n))==n for all n"""  
           for integer in range(1, 10000):  
                square=sqrteg.sqIt(integer)  
                result=sqrteg.sqrtIt(square)  
                self.assertEqual(integer, result)  

I took a fair range of numbers and applied both functions back to back to obtain the real one. We will check whether the result value is same as the original one.
So thats it. We finished our small test file for our simple program.

Now we start sqrteg.py
First of all we create Classes for the items we did in test file.
(Here in my test file a TypeError and OutOfRangeError).


 class SqrtegError(Exception): pass   
 class TypeError(SqrtegError):pass  
 class OutOfRangeError(SqrtegError): pass  

Here SqrtegError isn't necessary. We can directly add Exception in both TypeError and OutOfRangeError classes. But this is the standard way we follow.

First we just define the two functions with python reserved word 'pass'.

 def sqIt(n):  
      pass  
 def sqrtIt(n):  
      pass  

When we run sqrtegtest.py we can see six failures.
Then we gradually add the function and then provide checking conditions for bad input and eventually the failures will vanish one by one.

The final sqrteg.py will look like


 class SqrtegError(Exception): pass   
 class TypeError(SqrtegError):pass  
 class OutOfRangeError(SqrtegError): pass  
 def sqIt(n):  
      if type(n)==int or type(n)==float:  
           return n**2            
      else:       
           raise TypeError,"Invalid"  
 def sqrtIt(n):  
      if n<0:  
           raise OutOfRangeError,"No negatives please"  
      if type(n)==int or type(n)==float :  
           return n**.5  
      else:       
           raise TypeError,"Invalid"  


Finally you can see this 'ok' if  it passes the test.




Lot of other options are available in unittest module in python.

To see the sample programs used above, Click here