Friday, August 10, 2012

Unit Testing in Python


Unit testing is a method by which individual units within the code are tested to determine if they are fit for use and to see if they performs as expected. Each unit may be of different sizes and are the smallest testable part of a module.

Unit testing may be implemented simultaneously with the development of individual units. It can be done by creating an application to perform testing with all probable test cases. [more about unit tesing]

In Python Unit Testing can be done with the 'unittest' module.[see here]
Let me demonstrate it with a simple example.
The program that I'm testing is one which gives the numeric value corresponding to number given in words ie.. 'two hundred and twenty two' will become '222'.
Here's the code Wordnum.py 
A simple testing module for the above code is given below.
import Wordnum
import unittest

class WordNumCases(unittest.TestCase):
    knownvalues = (('zero', 0 ),
                   ('one', 1),
                   ('two', 2),
                   ('ten', 10),
                   ('thirteen', 13),
                   ('twenty one', 21),
                   ('hundred', 100),
                   ('hundred and seven', 107),
                   ('hundred and sixty nine', 169),
                   ('seven hundred and seventy seven', 777),
                   ('thousand', 1000),
                   ('one thousand', 1000),
                   ('thousand and two', 1002),
                   ('thousand and hundred', 1100),
                   ('SIx thoUSand FoUr HuNdReD and FIfTy NinE', 6459),
                   ('lakh and SEVenty five thousand ninety seven', 175097),
                   ('million and fifteen', 1000015),
                   ('ten crore ten lakh ten thousand ten hundred and ten', 101011010),
                   ('eighty five billion ninety nine lakh two hundred and five', 85009900205)
                 )

    def testWordNum(self):
        obj = Wordnum.convert()
        length = len(self.knownvalues)
        for word, number in self.knownvalues:
            answer = obj.create(word)
            self.assertEqual(answer, number)

class WordBadInput(unittest.TestCase):
    obj = Wordnum.convert()
    def testInvalidWord(self):
        obj = Wordnum.convert()
        self.assertRaises(Wordnum.InvalidWordError, obj.create, 'hunded fifty')
   
    def testNoInputGiven(self):
        obj = Wordnum.convert()
        self.assertRaises(Wordnum.NoWordGivenError, obj.con, '')

    def testRepetition(self):
        obj = Wordnum.convert()
        self.assertRaises(Wordnum.RepetitionError, obj.create, 'two thousand and three thousand')


if __name__ == "__main__":
    unittest.main()

The result of the script when run as python Wordnumtest.py is




The result says that the 4 tests defined in the test module for testing the above given code ran successfully producing the expected result and is fit to go.

Working
In the test module some known values is given as the test case in a class that inherits an object of type unittest.TestCase. Now the tests are carried by the unittest module through these test cases. The obtained result is then compared with the expected one (given in the test module itself). If both are same result says 'OK' otherwise an error is displayed.

In our Wordnumtest module known values are given as knownvalues in the class  WordNumCases. Certain test cases along with the expected result is given in it. These test cases are tested by using the function testWordNum(). The assertEqual() assertion in it checks if the obtained result and the expected one are the same.

Four tests are run using the module, first being the satisfactory result for the known values. Other three tests are used to determine if the code raises exception for the known errors.
Eg:  testInvalidWord()- This function checks if the given words describing the number is proper. Suppose that 'hundred and fifty' is unexpectedly given as 'hunded fifty', it should raise an InvalidWordError as given in the code Wordnum.py. It is done by the assertion assertRaises().

Other two tests are used for determining whether the code raises  NoWordGivenError and RepetitionError. The former is raised when no input (empty string or only 'and') is given. The  latter checks if the same powers of ten are repeated eg: 'three thousand and two thousand' (This test is given just to demonstrate the unittest module and may not be needed in the actual program unless faulty inputs are expected to be given).

Till now we discussed when the  test cases suffice. Now what if there is an error.
Let me deliberately introduce an error while checking the known values (returns '269' when 'two hundred and sixty five' is given). Here's what we get when the test is run.



We can that the assertion fails as AssertionError: 265 != 269 and hence the test fails showing that there is a bug in the code.

The above example is a simple one to demonstrate the unittest module in python. This can be extended to whatever code we develop by specifying the proper test cases. And it is a good practice to create test module before or along with development of each unit in the code rather than creating a unit test module at the time of integration testing.

Here's an example code that performs the opposite of the example given here.. 

No comments:

Post a Comment