The unittest framework is a built-in module in Python used to create unit tests to verify the correctness of your code. Unit tests are an essential part of the development process as they ensure that individual components of your code work as expected. Below are key features and example snippets for using the unittest module.
1. Basic Structure of a Unit Test
A unit test typically consists of a test class that inherits from unittest.TestCase, and test methods that verify the functionality of a specific piece of code.
import unittestclassMyTestCase(unittest.TestCase):deftest_addition(self): result =1+1self.assertEqual(result,2)# Verify the result is 2if__name__=='__main__': unittest.main()
Here, assertEqual is used to check if the result of 1 + 1 is equal to 2.
2. Common Assertions
assertEqual(a, b): Check if a and b are equal.
assertNotEqual(a, b): Check if a and b are not equal.
assertTrue(x): Check if x is True.
assertFalse(x): Check if x is False.
assertIsNone(x): Check if x is None.
assertIsNotNone(x): Check if x is not None.
assertIn(a, b): Check if a is in b.
assertNotIn(a, b): Check if a is not in b.
3. Test Setup and Teardown
Sometimes, you need to set up preconditions or clean up after tests. You can use setUp() and tearDown() methods for this purpose.
setUp(): Called before every test method.
tearDown(): Called after every test method.
4. Skipping Tests
You can skip tests conditionally using decorators like @unittest.skip() or @unittest.skipIf().
@unittest.skip(reason): Skip a test unconditionally.
@unittest.skipIf(condition, reason): Skip a test if the condition is true.
@unittest.skipUnless(condition, reason): Skip a test unless the condition is true.
5. Test Fixtures
Test fixtures provide a way to organize setup and teardown code for multiple tests, using setUp() and tearDown(), or class-level setup and teardown with setUpClass() and tearDownClass().
setUpClass(): Called once before all tests in the class.
tearDownClass(): Called once after all tests in the class.
6. Test Suites
A test suite is a collection of test cases. You can create a suite using unittest.TestLoader to load test cases from a class or module and then run them together.
7. Mocking with unittest.mock
The unittest.mock module allows you to replace parts of your system under test with mock objects. This is useful for testing code that interacts with external systems, like databases or APIs.
8. Handling Expected Exceptions
You can use the assertRaises() method to check that an exception is raised under certain conditions.
9. Running Tests from Command Line
You can run the tests from the command line by calling python -m unittest. This will discover and run all test cases in the module or directory.
10. Organizing Tests into Modules
It’s a good practice to organize tests into separate modules for different functionalities. You can organize tests by creating different test files and folders.
Each file can contain multiple test cases or test suites.
Summary of Key Features:
Assertions: Verify correctness using assertions like assertEqual(), assertTrue(), assertRaises(), etc.
Setup and Teardown: Use setUp(), tearDown(), setUpClass(), and tearDownClass() for preparation and cleanup.
Skipping Tests: Skip tests using @unittest.skip and conditional decorators.
Mocking: Use unittest.mock to simulate external systems.
Running Tests: Run tests via unittest.main() or the command line.
class MyTestCase(unittest.TestCase):
def setUp(self):
self.test_data = [1, 2, 3]
print("Setting up data.")
def tearDown(self):
print("Tearing down data.")
self.test_data = None
def test_addition(self):
result = sum(self.test_data)
self.assertEqual(result, 6)
if __name__ == '__main__':
unittest.main()
class MyTestCase(unittest.TestCase):
@unittest.skip("Skipping this test unconditionally")
def test_skipped(self):
self.fail("This test is skipped.")
@unittest.skipIf(True, "Condition met, skipping")
def test_skip_if(self):
self.fail("This test is skipped.")
if __name__ == '__main__':
unittest.main()
class MyTestCase(unittest.TestCase):
@classmethod
def setUpClass(cls):
print("Setting up class-level fixtures.")
@classmethod
def tearDownClass(cls):
print("Tearing down class-level fixtures.")
def test_method1(self):
print("Test method 1")
def test_method2(self):
print("Test method 2")
if __name__ == '__main__':
unittest.main()
def my_test_suite():
suite = unittest.TestSuite()
suite.addTest(MyTestCase('test_addition'))
suite.addTest(MyTestCase('test_assertions'))
return suite
if __name__ == '__main__':
runner = unittest.TextTestRunner()
runner.run(my_test_suite())
from unittest import mock
class MyTestCase(unittest.TestCase):
@mock.patch('builtins.print')
def test_mocking(self, mock_print):
mock_print("Hello")
mock_print.assert_called_with("Hello")
if __name__ == '__main__':
unittest.main()
class MyTestCase(unittest.TestCase):
def test_divide_by_zero(self):
with self.assertRaises(ZeroDivisionError):
1 / 0
if __name__ == '__main__':
unittest.main()