Testing - CameronAuler/python-devops GitHub Wiki
Testing is crucial in software development to ensure code correctness, reliability, and maintainability. Python provides a built-in unittest
module for writing and organizing tests.
Table of Contents
unitest
Unit testing involves testing individual functions or components in isolation. Pythonβs unittest
module follows a structured approach with:
- Test cases (
TestClass
with test methods) - Assertions (
assertEqual()
,assertTrue()
, etc.) - Setup and teardown (
setUp()
andtearDown()
)
Basic Unit Test
unittest.TestCase
creates a test case class. self.assertEqual()
verifies expected results. unittest.main()
runs all tests.
import unittest
# Function to test
def add(a, b):
return a + b
# Test case class
class TestMathOperations(unittest.TestCase):
def test_addition(self):
self.assertEqual(add(2, 3), 5) # Check if 2 + 3 = 5
self.assertEqual(add(-1, 1), 0) # Check negative numbers
if __name__ == "__main__":
unittest.main()
# Output (if all tests pass):
..
----------------------------------------------------------------------
Ran 2 tests in 0.001s
OK
unittest
Assertions
self.assertEqual(a, b) # Check if a == b
self.assertNotEqual(a, b) # Check if a != b
self.assertTrue(condition) # Check if condition is True
self.assertFalse(condition) # Check if condition is False
self.assertRaises(Exception, func, *args) # Check if an exception is raised
setUp()
& tearDown()
Using setUp()
runs before each test. tearDown()
runs after each test. Mainly used for preparing and cleaning up test environments.
class TestExample(unittest.TestCase):
def setUp(self):
self.test_data = [1, 2, 3] # Runs before each test
def test_length(self):
self.assertEqual(len(self.test_data), 3)
def tearDown(self):
self.test_data = None # Cleanup
Test Discovery & Organization
For larger projects, organize tests into directories and run them efficiently.
Unit Test Directory Organization
Store tests in a separate tests/
directory.
project/
βββ src/
β βββ math_operations.py
βββ tests/
β βββ test_math_operations.py
βββ main.py
unittest
Discovery
Running Tests with Automatically discover and execute all tests.
Run all tests in a directory:
python -m unittest discover tests
Run a specific test file:
python -m unittest tests/test_math_operations.py
Mocking & Patching in Unit Tests
Mocking replaces real objects with fake ones during testing which is useful for testing external dependencies (APIs, databases, file systems).
unittest.mock.Mock
Mainly used for mocking database queries, network requests, or expensive computations.
from unittest.mock import Mock
# Create a mock object
mock_obj = Mock()
mock_obj.some_method.return_value = "Mocked Response"
print(mock_obj.some_method()) # Output: Mocked Response
patch()
patch()
replaces a function or object with a mock only within the test scope. @patch("requests.get")
replaces requests.get
with a mock object. The API request does not actually happen, making tests faster and independent.
import unittest
from unittest.mock import patch
import requests
# Function that makes an API request
def fetch_data():
response = requests.get("https://api.example.com/data")
return response.json()
class TestAPI(unittest.TestCase):
@patch("requests.get") # Mock requests.get
def test_fetch_data(self, mock_get):
mock_get.return_value.json.return_value = {"message": "Success"} # Mock response
result = fetch_data()
self.assertEqual(result["message"], "Success") # Validate mock response
if __name__ == "__main__":
unittest.main()
# Output:
.
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
Mocking Classes & Methods
Mainly used for simulating class methods without modifying real code.
from unittest.mock import MagicMock
class MyClass:
def my_method(self):
return "Real Result"
mock_instance = MagicMock()
mock_instance.my_method.return_value = "Mocked Result"
print(mock_instance.my_method()) # Mocked Result