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

Hiding Unwanted Python Folders and Files in Visual Studio Code

Visual Studio Code is a universal editor and pretty good at it. However, the explorer view maybe cluttered with the automatically generated folders and files confusing developers. Python is no different. Below are example files and folders generated by Python.

The __pycache__ folder and *.pyc files  are totally unnecessary to the developer. To hide these files from the explorer view, we need to edit the settings.json for VSCode. Add the folder and the files as shown below:
Copy and paste the lines below :

"**/*.pyc":{"when":"$(basename).py"},"**/__pycache__":true

Get Started with MongoDB Stitch : The New Backend As Service Offering from MongoDB

Halfway of this year, the guys from MongoDB launch their new backend as service product called MongoDB Stitch. While the launch is just for the beta, the promise of the service is quite interesting. MongoDB has been around for long now and some development stacks have been based on its database product, the MongoDB-ExpressJS-Angular-NodeJS (MEAN) and the MongoDB-ExpressJS-React-NodeJS (MERN) stacks to name a few. These stacks, however, relies on backend technology such as ExpressJS and NodeJS. The idea of provisioning servers and developing the backend solution makes it daunting for small to medium scale applications. MongoDB Atlas, at least made life much easier by providing on cloud database solution, but there must be a simpler solution, right? A solution the would stitch the backend and frontend together ( see what I did there ?).
MongoDB Stitch lets developers focus on building applications rather than on managing data manipulation code, service integration, or backend infrastruct…

Getting Started with Stateless : A Lightweight Workflow Library Alternative for .NET

A year ago, I was looking for a simple workflow manager for a project I was working. Its a medium sized application that involves tracking the state of assets in the system. Back in 2008, Microsoft (MS) introduced new technologies along with the release of Visual Studio 2008: Windows Presentation Foundation (WPF), Windows Communication Foundation (WCF), and Windows Workflow Foundation(WF). Having worked in a company utilizing mostly MS products for development, my first option was to go with WF. After doing some time reading and studying the library, I paused and decided it was too complex for my requirement. Using WF would be an overkill and the fact that it has, a rather, steep learning curve, there has to be another option. My mind toyed with the idea of developing a simple workflow library myself. It would be a learning experience but it might end up consuming a lot of time.

Why reinvent the wheel? So I started querying the internet for a better solution. I stumbled upon Stateless