You are currently viewing Understanding Unit Testing in Python with the unittest Module

Understanding Unit Testing in Python with the unittest Module

Unit testing is a crucial part of software development, ensuring that functions and tasks within code work as intended. By dividing code into small units and testing them independently, developers can catch errors early and simplify the debugging process.

This article explores how to implement unit testing in Python using the unittest module.

Getting Started With unittest

The unittest module includes a number of methods and classes for creating and running test cases. Let’s look at a simple example where we used the unittest module to create a test case.

First, we imported the unittest module, which will enable us to use the classes that will be used to write and execute test cases.

The TestSample class is defined that inherits from the unittest.TestCase which will allow us to use the various assertion methods within our test cases.

We defined two test methods within our TestSample class: test_equal and test_search.

The test method test_equal() tests if round(3.155) is equal to 3.0 using the assertEqual() assertion method.

The test method test_search() tests if the character "G" is present in the string "Geek" using the assertIn() assertion method.

To run these tests, we need to execute the following command in the terminal.

This command will launch unittest as a module that searches for and executes the tests in the basic.py file.

Note: The unittest module only discovers and executes those methods that start with test_ or test.

unittest output

By the way, these dots represent a successful test.

We can use the unittest.main() function and put it in the following form at the end of the test script to load and run the tests from the module.

This will allow us to run our test file, basic.py in this case, as the main module.

Output

More Detailed Result

We can use the -v flag in the terminal or pass an argument verbosity=2 inside the unittest.main() function to get a detailed output of the test.

Vervosity added using -v flag

Commonly Used Assertion Methods

Here is the list of the most commonly used assertion methods in unit testing.

MethodChecks that
assertEqual(a, b)a == b
assertNotEqual(a, b)a != b
assertTrue(x)bool(x) is True
assertFalse(x)bool(x) is False
assertIs(a, b)a is b
assertIsNot(a, b)a is not b
assertIsNone(x)x is None
assertIsNotNone(x)x is not None
assertIn(a, b)a in b
assertNotIn(a, b)a not in b
assertIsInstance(a, b)isinstance(a, b)
assertNotIsInstance(a, b)not isinstance(a, b)

Example

Assume we have some code and want to perform unit testing on it using the unittest module.

The code defines a class called Triangle which has an init method that initializes the object with the instance variables self.base and self.height.

There are two more methods in the Triangle class: area() and perimeter().

The area() method returns the area of the triangle, which is half the product of the base and height (0.5 * self.base * self.height).

The method parameter() accepts a parameter called side, and because the triangle’s parameter is the sum of its three sides, the base and height variables take the place of the other two sides.

Now we can create another Python file in which we’ll write some tests and then execute them.

The above code imports the Triangle class from the triangle module (triangle.py file) as well as imports the unittest module to write test cases.

The TestTriangle class inherits from the unittest.TestCase which has four test methods. The Triangle class was instantiated with a base of 9 and height of 8 and stored inside the variable t.

The test_area method tests whether self.t.area() is equal to the expected result 36 using the assertEqual() assertion.

The test_perimeter method tests whether self.t.perimeter(5) is equal to 22 using the assertEqual() assertion.

The test_valid_base and test_valid_height methods are defined to test if the base (self.t.base) and height (self.t.height) of the triangle are greater than 0 using the assertGreater() assertion.

The unittest.main(verbosity=2) method retrieves and executes the tests from the TestTriangle class. We’ll get a detailed output because we used the verbosity=2 argument.

TestTriangle class output

Test for Exception

If you’ve used assert statements before, you’ll know that when one fails, it throws an AssertionError. Similarly, whenever a test method fails, an AssertionError is raised.

We can predetermine the conditions under which our code will generate an error, and then test those conditions to see if they generate errors. This is possible with the assertRaises() method.

The assertRaises() method can be used with context manager so we’ll use it in the following form:

Consider the following function gen_odd(), which generates a series of odd numbers up to the argument n by incrementing the num by 3 and contains only a few checks, where the argument n must be of type int and greater than 0.

Now we’ll write test methods to simulate conditions that could cause the above code to fail.

We wrote three test methods in the OddTestCase class to ensure that when invalid arguments are passed, the corresponding error is raised.

The test_negative_val() method asserts that ValueError is raised when gen_odd(-5) is called.

Similarly, the test_float_val() and test_string_val() methods assert that when gen_odd(99.9) and gen_odd('10') are called, respectively, TypeError is raised.

OddTestCase output

All three tests in the above code passed, which means they all raised corresponding errors, otherwise, the tests would have failed or raised the errors if another exception was raised. Let’s put it to the test.

The above condition within the test_valid_arg() method will not throw a TypeError because gen_odd() function is passed with a valid argument.

Output

The above test method failed and raised an AssertionError with the message TypeError not raised : Valid argument.

Skipping Tests

The unittest makes use of the skip() decorator or skipTest() to skip any test method or whole test class on purpose, and we are required to specify the reason why the test is being skipped.

Consider the previous example’s code, which we modified by adding the skip() decorator.

It’s clear that the above condition will fail and throw an AssertionError, so we skipped the testing on purpose.

Skipped test output

What if we wanted to skip the test if a particular condition was true? We can accomplish this by using the skipIf() decorator, which allows us to specify a condition and skip the test if it is true.

The condition in the above skipIf() decorator checks whether the size of gen_odd(10) is greater than 10 bytes, if the condition is true, the test method test_memory_use() is skipped, otherwise, the test is executed.

Output

Expected Failure

If we have a test method or test class with conditions that are expected to be false, we can use the expectedFailure() decorator to mark them as expected failures instead of checking for errors.

We’ve modified the previous code and the condition we are checking inside the test_memory_use() method is expected to be false, which is why the method is decorated with the @unittest.expectedFailure decorator.

Expected failure output

Conclusion

We can use the unittest module to write and run tests to ensure that the code is working properly. The test can result in one of three outcomes: OK, FAIL, or Error.

The unittest module provides several assertion methods that are used to validate the code.

Let’s recall, what we’ve learned:

  • the basic usage of unittest module.
  • CLI commands to run the tests.
  • testing if the condition is raising an exception.
  • skipping the tests on purpose and when a certain condition is true.
  • marking a test as an expected failure.

πŸ†Other articles you might be interested in if you liked this one

βœ…How to use assert statements for debugging in Python?

βœ…What are the uses of asterisk(*) in Python?

βœ…What are __init__ and __new__ methods in Python?

βœ…How to implement getitem, setitem and delitem in Python classes?

βœ…How to change the string representation of the object using str and repr methods?

βœ…How to generate temporary files and directories using tempfile in Python?

βœ…Build a custom deep learning model using the transfer learning technique.


That’s all for now

Keep coding✌✌