Unit testing in Python

As Python is a dynamic language, unit testing is not only important for verifying code works as expected, but also needs to cover situations that statically typed languages get for free. So a high level of test coverage is useful to ensure event basic methods are working and even named as expected.

Let’s create a simply Calculator class which we will then write unit tests for (this is saved in a file named calc.py)

class Calculator:
    def add(self, a, b):
        return a + b

We can then create a file test_calculator.py and within this we have the following

from unittest import TestCase
from calc import Calculator


class TestCalculator(TestCase):
    def test_add(self):
        c = Calculator()
        self.assertEqual(5, c.add(3, 2))

As you can see, we need to derive our test class from the TestCase class and test methods should use the naming convention test_ at the start of the test method’s name.

Note: the class name does not require the Test prefix/naming convention.

Using PyCharm we need to create a new configuration, from the Python tests configuration section. Select nosetests and then name the configuration, something like Calculator Tests.

Now we can run the tests. If you find nosetest is not installed you can run the Python Console from PyCharm and run the command

pip install nose

setUp/tearDown

Like most unit testing frameworks, the TestCase class can use a setUp and/or tearDown method for setting up the test case context and cleaning up after each test method is run.

For example

class CalculatorTests(TestCase):

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

    def test_add(self):
        self.assertEqual(5, self.c.add(3, 2))

    def test_subtract(self):
        self.assertEqual(2, self.c.subtract(7, 5))

setUpClass/tearDownClass

TestCase also includes setUpClass and tearDownClass for setting context for all test methods within a TestCase.

For example, here’s the previous TestCase but using the setUpClass to create the calculator for all test methods

class CalculatorTests(TestCase):

    @classmethod
    def setUpClass(cls):
        cls.c = Calculator()

    def test_add(self):
        self.assertEqual(5, self.c.add(3, 2))

    def test_subtract(self):
        self.assertEqual(2, self.c.subtract(7, 5))

Note: the @classmethod is required in this example code or the setUpClass is not treated as a class method as is expected.

References

unittest — Unit testing framework
Creating and running a Python unit test