Skip to main content

Unit Testing in Python : A Primer


Automated unit testing has been around for more than a decade now. I remember back in 2008, a colleague introduced to our team the concept of MVP pattern and in the process the advantage of using the pattern in unit testing. The concept of automated testing using NUnit and mocking using NMock was also discussed. That is where I began to see the advantage of implementing Test Driven Development methodology to developers. Unfortunately, the company where I moved don't see it as such. Thus, I was only able to practice it on my learning time. With the recent events in my life , I now have more time exploring this concept a little further. A series of post will concentrate on this concept using Python and C#. Lets begin with Python.

Directory Structure

Lets begin with simple unit test using unittest. For the code samples, we will use the following directory structure :

+UnitTestingSample\
+---+Modules\
+-------+Calculator\
+------------__init__.py
+------------main.py
+--------_init__.py
+---+UnitTest\
+--------__init__.py
+--------test_calculator.py
+----setup.py

I organized my codes in such a way that the actual application code is separated from the unit test codes.  For this to work unit test discovery will be used if you are to run it on the terminal. In my case, I run the tests inside PyCharm.

The Application

To make this as simple as possible,  I will use a simple Calculator application with the following methods to be tested :
  • add(num1, num2) - Add two numbers
  • subtract(num1,num2) - Subtraction
  • multiply(num1,num2) - Multiplication
  • divide(num1,num2) - Division

The Unit Test


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import unittest
from Modules.Calculator.main import Calculator


class MyTestCase(unittest.TestCase):

    def setUp(self):
        self.app = Calculator()

    def test_add(self):
        expected = 5
        result = self.app.add(3,2)
        self.assertEqual(result,expected)

    def test_subtract(self):
        expected = 5
        result = self.app.subtract(10,5)
        self.assertEqual(result,expected)

    def test_multiply(self):
        expected = 5
        result = self.app.multiply(5,1)
        self.assertEqual(result,expected)

    def test_divide(self):
        expected = 5
        result = self.app.divide(10,2)
        self.assertEqual(result,expected)

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

If you are accustomed on creating unit test in .Net, the names of the test file as well as test methods are insignificant . Aside from the general rules in naming files and method names, theres nothing special to be considered. In Python, however, its more strict. Below are the two rules that are significant for the tests to be discovered:

  • The file name of the test file should start with "test: (i.e.  test_calculator.py)
  • The method names should start with test ( i.e.  test_add)
Since we are using unittest, all test classes should inherit from unittest.TestCase. For the example, the assertEqual is used. Complete documentation on Python 3 regarding unittest can be found in this link

Running the Test

To run the script, we can use the terminal or command prompt to run discovered unit test using the following command :

TrashvinMBP:UnitTestingSample Trashvin$ python -m unittest discover -v 

Successful Test

TrashvinMBP:UnitTestingSample Trashvin$ python -m unittest discover -v      
test_add (UnitTests.test_calculator.MyTestCase) ... ok
test_divide (UnitTests.test_calculator.MyTestCase) ... ok
test_multiply (UnitTests.test_calculator.MyTestCase) ... ok
test_subtract (UnitTests.test_calculator.MyTestCase) ... ok

----------------------------------------------------------------------
Ran 4 tests in 0.000s

OK

Failed Test 

TrashvinMBP:UnitTestingSample Trashvin$ python -m unittest discover -v
test_add (UnitTests.test_calculator.MyTestCase) ... ok
test_divide (UnitTests.test_calculator.MyTestCase) ... FAIL
test_multiply (UnitTests.test_calculator.MyTestCase) ... ok
test_subtract (UnitTests.test_calculator.MyTestCase) ... ok

======================================================================
FAIL: test_divide (UnitTests.test_calculator.MyTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/Trashvin/Dev/Codes/Python/UnitTestingSample/UnitTests/test_calculator.py", line 30, in test_divide
    self.assertEqual(result,expected)
AssertionError: 6 != 5

----------------------------------------------------------------------
Ran 4 tests in 0.001s

FAILED (failures=1)

Running test in the command line / terminal is far less aesthetically appealing that its Visual Studio equivalent but some Python editors such as PyCharm can provide such experience too.


Conclusion

Automated unit testing provide developers tool of easily testing their code without actually using the application. While writing unit test seems to add time at the start, on the long run, it helps the developer maintain checks on the codes that they write. For a continuously developing application, this provides a way to make sure that old codes are not broken. Unit testing, however, is not a complete solution.  On the next related post, I will discuss the concept of mocking.

Resources

Comments

Popular posts from this blog

renaming default namespaces for VSTO projects in VS2008

So here is the scenario , you are starting a VSTO project and decided that your default namespace is ExcelAddInTesterApp . You created the project and started coding the project. After several days , your boss called and said "hey marvin , make use of this namespace OurCompany.ExcelAddInTesterApp , we have to add our company name to it got it?" . You get back to your machine thinking its just a simple property just like any project you've been working on. So you right clicked the VSTO project and hit properties . Boom! What the F@#$? The default namespace textbox is disabled!!!!



I've been through this and I googled for ways to do it and ended up with a blog from a Microsoft MVP telling me it can't be done because it is disabled. Then I thought of Refactoring, the beauty and grandeur of the renaming process. I selected the namespace and hit the refactor menu hoping that this would solve the problem . Unfortunately , it did not rather it displayed the message box …

this year

.. i will learn new things, perhaps play a new instrument or go back to school.
.. i will find beauty on the open source community.
.. i will read more books and listen to more music.
.. i will pen more poems and possibly write more songs.
.. i will go for new distances and discover new terrains.
.. i will continue to pile running miles to stay fit and healthy.
.. i will strive to be free and make myself available to unknown future.
.. i will go out of my shell more often and be socially more open.

.. i will continue to grow and go with the changes of time.
.. i will be more patient and hopeful to things that may not even be possible.
.. i will be a better son, brother and more importantly a better father.
.. i will be closer with the great beyond.

.. i will continue to repair , refactor and upgrade myself to become a better version of me.

Farewell NU107.5

As I write this blog,  millions of Filipino rock fanatics may have already known what happens after 12:00 midnight tonight. The only surviving station that caters to the rock genre will, sadly, be closing down and permanently signing off tonight. The news came to late to enjoy every second of what is left.  The sad news came to me the last week of October.  Being a follower of the station, I never believed it at first. But online resources would say the station really is nearing its demise.  With several minutes to go, it will be dead air.