Updated October 7, 2023
Introduction to Unit Testing in Python
Unit Testing in Python is crucial for ensuring that software units, whether modules, functions, or interfaces with other modules, perform their intended functionality as designed. Typically conducted by developers during the code development phase, unit testing allows them to create comprehensive test cases that cover various scenarios encountered during real-time execution. Developers can quickly and repeatedly test their code through automated and documented testing processes. Unit testing involves assessing different code blocks responsible for specific functions, helping identify and prevent potential errors that may arise from improper inputs. Unit testing verifies individual code components before software testing. Python simplifies this process by offering a built-in module that developers can import, providing access to various built-in methods for conducting diverse tests.
Table of Contents
Understanding Key Concepts in Unit Testing
We need to write different test cases and import our code file into our test case file for testing our code. Unit test modules support different Oops concepts for testing the code.
- Test Fixture: We use a test fixture to define the specific environment in which we need to carry out the test, such as creating temporary databases.
- Test Cases: Test cases are specific conditions written to assess the code, yielding only three possible results.
- OK: It means all the test cases are successfully passed without any error, and our code is fine.
- Fail: It means the AssertionError exception is generated. Our code generates it when it fails during the test case.
- Error: It means our test case has some error instead of AssertionError.
- Test Suite: Test suites consist of test cases written in a file.
- Test Runner: The test runner is responsible for carrying out the test and writing the result to the user.
How does Unit Testing work in Python?
Upon completing a unit of code in Python, the developer is supposed to test the coding unit to ensure that:
- The program has no bug, and it works well for all possible test conditions correctly.
- It correctly takes the inputs from upstream software units and properly passes the results to the downstream units.
Python Developers can resort to manual testing methods to verify the code, but it:
- It is a time-consuming task.
- Leaves fatigue.
- Do not document the test data and results in a structured way.
- It is not repeatable and can make maintenance tedious work.
Hence, Python developers will have to create scripts that can be used in future testing during program maintenance. Python offers a unit testing framework unit test for the developers to automate the testing process.
Examples of Unit Tests in Python
The examples are given below:
def addition(x, y): return x + y def subtraction(x, y): return x-y def multiplication(x, y): return x * y def division(x, y): return x / y
This is a very basic program that does addition, subtraction, multiplication, and division. Save the above in the calc.py file. Now according to the rules of the unittest module of Python, we have to create the test file for our code file. It can be named test_calc.py or calc_test.py; both are good naming conventions. So, we are saving our file with the test_calc.py name in the same folder as our original file.
import unittest import calc class TestCalc(unittest.TestCase): def test_addition(self): self.assertEqual(calc.addition(5, 5), 10) self.assertEqual(calc.addition(-3, 3), 0) self.assertEqual(calc.addition(-5,-5),-10) def test_subtraction(self): self.assertEqual(calc.subtraction(15, 5), 10) self.assertEqual(calc.subtraction(-1, 2),-3) self.assertEqual(calc.subtraction(-1,-1), 0) def test_multiplication(self): self.assertEqual(calc.multiplication(20, 5), 100) self.assertEqual(calc.multiplication(-2, 1),-2) self.assertEqual(calc.multiplication(-1,-3), 3) def test_division(self): self.assertEqual(calc.division(10, 10), 1) self.assertEqual(calc.division(-1, 1),-1) self.assertEqual(calc.division(-2,-2), 1) self.assertEqual(calc.division(6, 3), 2) if __name__ == '__main__': unittest.main()
We have to write the above program in the test_calc.py file. We have written many test cases in this for all the 4 methods. We have imported the unittest modules and our calc program. We have created TestCalc and inherited the TestCase from the unittest module. It will provide us access to all the different testing methods from unittest. Now, we have defined the method name. Method names should be the same as method names from the original name followed by test_. It is essential to follow the naming convention; other methods will not run.
We have passed the self parameter like other methods. We used the assertEqual method to check whether the first parameter equals the second one. If it is equal, it will return ok, which means the test case is successful.
As you can see, we have called the calc. Add method and pass two parameters, as it takes two parameters as input and returns the addition of both. We have passed 5,5 and passed 10 as the second output of the assertEqual method. Now, it will compare with the sum of 5,5 with 10. It’s equal so that the output will be ok. Our test case was successful. We will conduct unit tests for all test cases and report the results as passing or failing.
“__name__” is the Python’s built-in module, and it will return the module’s name. If another module uses our file, the name will be assigned to the name variable. Currently, executing the __name__will return __main__ as the module’s name. So, if the condition becomes true and unittest, it will perform all the methods inside the main method, i.e., self.
Various Test Cases with Examples
Given below are the various test cases with examples:
1. Using simple Python code
Assuming a developer is creating a software unit to determine the square root of a number and the code for this function,
# function to compute square root of a number import math # importing Mathematics module def square_root(l): # Function beginning return math.sqrt(l) # Returning results
A simple way of manual testing will be to write a code.
print ("square root of 64 ",square_root(64))
Another test case is:
print ("square root of 64A ",square_root("64A"))
Failed result is:
Here, the user will have to preserve test codes for future testing.
2. Using the assert command in Python
The Assert command compares the result with the given value and returns an error if the condition is unmet.
assert square_root(64) == 8 , "should be 8" #Code will not result in any error
assert square_root(64) == 7 , "should be 8" #will return error condition
The developer can code multiple test cases, but the execution will stop on the first error.
3. Multiple test cases using assert
import math def square_root(x): return math.sqrt(x) def test_sqrt(): assert square_root(64) == 8, "should be 8" assert square_root(81) == 9, "should be 9" assert square_root(100) == 10, "should be 10" if __name__ == "__main__": test_sqrt() print("Everything passed")
All the test cases are put in a Python function and executed under __name__ == “__main__” condition. The module is run in standalone mode directly within the code and not imported from an external repository.
import math def square_root(l): return math.sqrt(l) def test_sqrt(): assert square_root(64) == 8 , "should be 8" assert square_root(81) == 9 , "should be 9" assert square_root(100) == 11 , "should be 10" if __name__ == "__main__": test_sqrt() print("Everything passed")
Though a little bit of automation with multiple test cases is possible in this method, it does not provide comprehensive test results of how many patients have failed and how many have passed. We can only manage simple cases with this method. Test runners provide a special application for easy execution of test cases and publish a clear result of no of passed and failed cases.
Test Runners in Python
Unit test is an inbuilt test runner within Python.
Unique features of this test runner are:
- Test conditions are coded as methods within a class.
- Allows various assert methods from the unittest library against a simple assert statement in the earlier examples.
- Unit test library should be imported.
- Create a Testclass class by inheriting the Testcase class in the unittest library.
- Add var as the first argument in all the methods in test functions.
- Replace assert with var.asssert.equal method in the Testcase class.
- Unittest.main() is the entry point.
import math import unittest def square_root(l): return math.sqrt(l) class Testclass(unittest.TestCase): def test_case1(var): var.assertEqual(square_root(121), 11, "Should be 11") def test_case2(var): var.assertEqual(square_root(144), 12, "Should be 12") def test_case3(var): var.assertEqual(square_root(169), 13, "Should be 12") def test_case4(var): var.assertEqual(square_root(196), 14, "Should be 12") def test_case5(var): var.assertEqual(square_root(225), 15, "Should be 12") def test_case6(var): var.assertEqual(square_root(256), 16, "Should be 12") if __name__ == "__main__": unittest.main()
The result is (all the 6 cases are correct):
When the code is changed as:
import math import unittest def square_root(l): return math.sqrt(l) class Testclass(unittest.TestCase): def test_case1(var): var.assertEqual(square_root(121), 11, "Should be 11") def test_case2(var): var.assertEqual(square_root(144), 12, "Should be 12") def test_case3(var): var.assertEqual(square_root(169), 13, "Should be 12") def test_case4(var): var.assertEqual(square_root(196), 14.3, "Should be 12") def test_case5(var): var.assertEqual(square_root(225), 15.2, "Should be 12") def test_case6(var): var.assertEqual(square_root(256), 16, "Should be 12") if __name__ == "__main__": unittest.main()
The result (with 2 errors):
Results clearly show the number of cases tested and no of cases that failed.
Other assert methods available in Python:
- AssertAlmostEqual: With rounding off at the 5th decimal, the comparison is done.
- AssertTrue(x): Boolean expression x is true when evaluated.
- AssetFalse(x): Boolean expression x is false when evaluated.
- Assertequal(a,b): a = b
- AssertIn(a,b): a in b
- AssertIs(a,b): a is b
- AssertIsNone(a): a is null
Other test runners:
There are some more test runners apart from the built-in tool unit test.
- Consider the nose as the extension of the unit test.
- Pytest has backward compatibility with minimal code.
- A hypothesis has a quick start and covers edge cases.
- Mimesis can generate artificial data that are useful for testing.
- Testify is similar to pytest.
Python offers robust support for performing unit testing and streamlining the process for convenient code maintenance by developers. With Python’s built-in module for unit testing, there’s no reliance on third-party tools, as the inbuilt capabilities suffice. It is highly recommended to conduct thorough unit tests using these built-in modules. It’s a best practice to perform unit testing on code before deploying it to either the development or production server, as this practice helps minimize the effort required for testing at the product level.
We hope that this EDUCBA information on “Unit Testing in Python” was beneficial to you. You can view EDUCBA’s recommended articles for more information.