Showing posts with label Unit. Show all posts
Showing posts with label Unit. Show all posts

Friday, June 30, 2017

Python unittest


Unit testing is an automated testing technique used to check small pieces of a project, usually called units. These units are defined by the developer so it can be from a tiny function to a whole functionality. However, there are some basic rules that every single test should follow:
  1. It has to be able to run independently.
  2. It has to run on memory
  3. It has to be consistent (always returns the same result for the same input)
  4. It should be fast
Those rules must be fulfilled to be able to run any unit at any time since it is needed to run the tests properly.
The basic idea of unit testing is to run the test every time the developer makes substantial changes to the code. By doing this you will know if a unit is still working or not after those changes, reducing the time and effort spent on bug fixing.

Unittest

Running simple tests

Unittest is a standard python library, which allows you to define and run tests in an easy way. Let’s start with a very simple example:
from unittest import TestCase, main


class TestBasicArithmeticMethods(TestCase):

    def test_add(self):
        self.assertEqual(2 + 2, 4)

    def test_subtract(self):
        self.assertEqual(3 - 2, 1)

    def test_multiply(self):
        self.assertEqual(3 * 3, 9)

    def test_equal(self):
        self.assertTrue(3 == 3)
        self.assertFalse(2 == 9)

if __name__ == '__main__':
    main()
First things first, what is defined here is a test case, which is the smallest part which can be defined using unittest. The only thing you have to do is defining unittest.TestCase as a superclass.
A test case will look for any method starting with “test_” and run it when you run the test case. Also, if you want to define only one test you can override the runTest method, which is the simplest way to do it.
The "if" statement at the bottom runs the tests every time you compile the code, which is very useful to check that recently added code does not change the correct functionality, as it was mentioned in the introduction.

Loading information

These tests are really simple and don’t need additional information to work, but in some cases, you need to load data from disk. Loading information every time you need it is inefficient and, as it was said before, the tests should run fast.
For that matter, unittest implement the setUp and tearDown methods, which allows you to load information before the tests, and then erase it after they are executed. Here you have an example:
from unittest import TestCase, main

from complex import Complex



class TestComplexArithmetic(TestCase):

    def setUp(self):
        self.zero = Complex()

    def test_add_complex(self):
        one_onei = Complex(1, 1)
        result = one_onei + self.zero
        self.assertEqual(result.r, 1)
        self.assertEqual(result.i, 1)

    def test_sub_complex(self):
        one_onei = Complex(1, 1)
        result = self.zero - one_onei
        self.assertEqual(result.r, -1)
        self.assertEqual(result.i, -1)

    def tearDown(self):
        del self.zero
As you can see here, the setUp method defines a private attribute called zero, which is used on all of the test cases. Then, that value is deleted after all the test cases are finished.
In this case, the zero value could be defined in every case because it isn’t a time-consuming operation, it is just added as an example. In general, in the setUp method, you will make queries to a database or pre-process information. Then, it will delete that information on the tearDown method to avoid filling the memory up with unnecessary data.

Skipping tests

Sometimes it is necessary to skip some tests after they are checked, for example, if they take a lot of resources, or if they are not supported by older libraries.
In that cases, you can use the unittest decorators skip and skipIf. The first decorator receives optional parameters only, where the most important is the message shown when the test is skipped. The second one needs a boolean value to check if the test is skipped or not, and then optionally the message. Let’s see an example:
from unittest import TestCase, skip, skipIf

from complex import Complex


class TestComplexArithmetic(TestCase):

    def setUp(self):
        self.zero = Complex()

    @skip("Sure that this is correct, and will never change")
    def test_default_complex_value(self):
        self.assertEqual(self.zero.r, 0)
        self.assertEqual(self.zero.i, 0)

    @skipIf(Complex.__version__ < (1, 1), "Not supported by this library")
    def test_equal_complex(self):
        zero = Complex()
        one = Complex(1)
        self.assertTrue(zero == self.zero)
        self.assertFalse(self.zero == one)

    def tearDown(self):
        del self.zero
On this example, you can see that the first test is always skipped with the skip decorator, which is used in general for time-consuming tests that have already been passed. Then the second test is skipped for older versions of the library, very useful when you use libraries in development or different libraries depending on the virtual environment.
Congratulations! You now have all the tools to do unit testing on your Python projects.
You can check a sample project here.