Saturday, June 18, 2016

Unit Testing and Code Coverage in Python

Unit Test is one of the most important part of development artifacts, which helps to automatically test whatever code we write. Coverage report measures quality of Unit Tests and points out unused/untested code. This tutorial will help to get started with Unit Testing and Coverage report in Python.
For this tutorial we will use Eclipse PyDEV as Python IDE and its integrated Coverage plugin to generate Code Coverage report.

Environment Setup:
  1. Install PyDEV Eclipse Plug-in. PyDEV comes with Code Coverage feature (Show View - Code Coverage) which integrates with python module "coverage"
  2. Install Coverage module:  
# python -m pip install "coverage < 4.0.0"
Coverage module higher than version 3.x doesn't work with PyDEV. Coverage report gets generated as binary file under-
Workspace\.metadata\.plugins\org.python.pydev.debug\coverage\.coverage
Sample Code: 
Download sample code from GitHub. This includes a function (under /src) to get ODD and EVEN numbers for a supplied Count. If supplied count is ODD, it returns all ODD numbers and vice versa. For this implementation, we have written Tests (under /test) which validates count of numbers returned. Count of numbers returned will be half of Supplied Count e.g. Supplied Count 100, it'll return 50 EVEN numbers.

Project Structure:
 
Create Python Project ("UnitTest") and then create 2 Source Folders under this Project- "src" and "test". Source codes are kept under "src" and respective Unit Test scripts under "tests" Folder. Unit Test scripts are created using same Package structure as of the actual script but Module name prefixed with "test_".

Unit Tests:
Python (since 2.1) includes Unit Test package (unittest). It helps to test scenarios of functions/classes and assert conditions. We'll cover more about unittest functionality in upcoming tutorials. In this session, I'll cover very simple assertion (assertEqual), which validates equality.

import unittest
import me.pras.data as data

class Test(unittest.TestCase):

    def testData(self):
        count = 9
        expectedCount = count/2
        nums = data.getNumbers( count )
        self.assertEqual( len( nums ), expectedCount, 'Count mismatch...')

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

Above code validates functionality of me.pras.data.getNumbers() function. If we run this Test (Right click on test_data.py - Run As - Python unit-test)

Coverage Report:
Once we execute above unit test, internally it generates Coverage Report (.coverage File). Now lets open "Code Coverage"view (Window - Show View - Code Coverage).
Drag and Drop our Project "UnitTest" at the bottom area of "Code Coverage" view.
 

If you have already run the Unit Tests, it'll show report on right-hand side of "Code Coverage" view. As you can see, we are passing only EVEN count as a part of Unit Test, so it has marked else section and _odd() functions as untested.

Isn't it Cool ? It points out any untested code and measures accuracy of your Unit Tests.