0x03. Unittests and Integration Tests - TLDR
Unit Tests - Back-end - Integration tests memoization - mock - specialization - advanced
Intro
Unit testing
Focuses on testing individual units or components of your code in isolation. A unit in this context is typically a small, self-contained piece of code, like a function or method.
Mocking: To isolate the unit being tested, you may need to mock (simulate) the behavior of external dependencies. For example, if a function makes a network request, you might mock the network call to control the input and output.
Integration Testing: The primary goal of integration testing is to ensure that different parts of your application work correctly together when integrated.
Integration tests involve multiple units, and they may include real external dependencies like databases, network services, or file systems. Low-level functions that make external calls are often not mocked in integration tests.
For example, an integration test might involve sending a request to an API endpoint and checking the response.
Unit Testing in Python
In Python, this is often done using the built-in unittest module.
Here's a basic example to illustrate this:
Suppose you have a simple function in Python that adds two numbers:
assertRaises:
In Python's unittest framework, self.assertRaises is used to test that a specific error is raised when you expect it to. This is particularly useful when you want to ensure that your code correctly handles invalid inputs or situations that should result in an error.
We use a
withstatement along withself.assertRaises(KeyError). This means we expect aKeyErrorto be raised whenaccess_nested_mapis called with the givennested_mapandpath.If a
KeyErroris indeed raised, the test passes. If no error or a different type of error is raised, the test fails.
parameterized:
parameterized package in Python, which is used for parameterizing tests, especially with Python's unittest framework. It allows you to run a single test method with multiple sets of data, helping you to test different scenarios efficiently without writing multiple test methods.
First, you would need to install the parameterized package if you haven't already:
pip install parameterized # terminal
# Don't forget in script to :
from parameterized import parameterizedThen, in your test code, you can use it as follows:
As you can see in example
Provide Test Cases as Lists of Tuples: When using
@parameterized.expand, you need to provide a list of tuples, where each tuple contains the arguments for one execution of the test method.
@parameterized.expand([
(arg1_test1, arg2_test1, ...),
(arg1_test2, arg2_test2, ...), ...
])Using assertRaises with parameterized :
You can use assertRaises within your parameterized test to check if a specific exception is raised when calling a function with certain inputs. Here's how you can do it:
In this example, the divide function will be tested with three sets of input data. For the first two sets, the test checks whether the output of the divide function matches the expected output. For the third set, which causes a division by zero, the test checks whether a ZeroDivisionError is raised [0]
Mocking in Unit Tests:
Mocking is primarily used in situations where the real objects are impractical to incorporate into the unit test. For example, you might want to test a function that interacts with a database, but it would be impractical to set up a real database for each test. Instead, you could create a mock database object that behaves like a real database but is easier to control and manipulate in a test environment.
Why Mock HTTP Requests?
mocking HTTP requests is crucial because you don't want your tests to depend on external services. External services can be unreliable, slow, or produce variable results.
HTTP requests are the way in which clients (like a web browser or your Python script) communicate with servers.
Python has a library named requests which is commonly used to make HTTP requests. The requests.get method is used to send a GET request to a specified URL.
unittest.mock.patch
unittest, has a sub-module mock.
The mock.patch function is used to replace the real HTTP request with a mock object. This allows you to control the responses and test how your function handles them.
To make HTTP GET requests to a specified URL and then parse the response as JSON. The typical implementation of such a function would use the requests library to make the HTTP call and return the .json() from the response.
import requests
def get_json(url):
response = requests.get(url)
return response.json()
Setting Up Mocking with unittest.mock.patch:
When testing, you want to avoid making actual HTTP requests. Instead, you can use unittest.mock.patch to replace requests.get with a mock that simulates its behavior.
Here's how to do it:
mock_get: This is your mock object that replaces therequests.getmethodreturn_value: This is an attribute of the mock objectmock_getthat specifies what the mock should return when it is called. Sincerequests.getreturns a response object,mock_get.return_valuesimulates this response object.

unittest.mock.patch to mock the requests.get method and make it return a Mock object with a json method.Configuring the Mock
mock_get.return_value.json.return_value = t_payloadWhat's Happening: mock_get is a fake version of requests.get created by the @patch decorator. This line tells mock_get what to return when it is called.
mock_get.return_valueis the fake response object thatmock_get(our fakerequests.get) returns when it's called..json.return_valueis the result of calling the.json()method on this fake response. We're setting this tot_payload, which means "when theget_jsonfunction calls.json()on the response, returnt_payload."
mock.patch along with @parameterized.expand
To use mock.patch along with @parameterized.expand, the @mock.patch decorator must come below the @parameterized.expand decorator, and the mocked parameters must come last. Here's an example:
In this example, the fetch_json function will be tested with two different URLs. For each URL, the test checks whether the output of the fetch_json function matches the expected output and whether the requests.get function was called with the correct URL
So Steps are:
Setup the mock to return a response object with a .json() method that returns
test_payloadCall the function result = get_json(test_url)
Make Assertions
memoize Decorator
A memoize decorator in Python is used to optimize a function that performs expensive computations. It does this by remembering (or "memoizing") the results of these computations so that if the function is called later with the same arguments, it can simply return the stored result instead of recalculating it
Let's start by defining a memoize decorator

memo Function: This is a decorator that takes a function func as its input and returns a new function memoized that wraps func. The purpose of memoized is to cache the results of calling func with specific arguments to avoid repeated calculations for the same inputs.
memo function (decorator) saves the result of each unique call to the fibo function in a cache, a dictionary where each key-value pair corresponds to an input (key) and its computed Fibonacci number (value). This caching mechanism is what enables memoization to significantly reduce the number of computations needed to calculate Fibonacci numbers, especially for larger values of n.Unit testing a memoized function, especially something like a memoized Fibonacci function, involves verifying that:
The function returns correct results for given inputs.
The
memoizationaspect works correctly, i.e., it actually caches computed results and uses them, reducing the number of times the function is actually executed.
Example : ensuring that the
memomethod is called only once when thefibofunction is called twice with the same argument
To unittest the memoization functionality, especially focusing on ensuring that the memo method is called only once when the fibo function is called twice with the same argument, you can use the unittest framework along with unittest.mock.patch. This approach allows you to intercept and monitor calls to specific parts of your code (in this case, the memo method) and assert that they behave as expected.
Step-by-Step Explanation
Patch the
memoDecorator: Useunittest.mock.patchto replace the realmemodecorator with a mock object. This allows you to track how many times thememodecorator is actually invoked.Call the Function: Call the
fibofunction twice with the same argument.Assert Calls: Use
assert_called_onceto verify that thememodecorator (now a mock object) was called exactly once, indicating that the result of the first call was correctly cached and reused for the second call without re-invoking the memoization logic.
mock_memo.assert_called_once() asserts that the mock memo function was called exactly once during the test. Since the fibo function is supposed to be memoized, this assertion checks that the function didn't unnecessarily recompute the value of fibo(5) on the second call
mock with memoize:
To use mock.patch with a function that uses memoize, you can follow a similar approach to mocking any other function. Here's an example:
In this example, the expensive_operation function is decorated with lru_cache to simulate memoization. The test_expensive_operation function is decorated with @parameterized.expand and @mock.patch. The @mock.patch decorator replaces the expensive_operation function with a mock function for the duration of the test. The mock function always returns the expected output. After the test function runs, it checks whether the output of the expensive_operation function matches the expected output
Mock a property:
To mock a property in Python, especially when using the `unittest.mock` module, you can use the `PropertyMock` class in conjunction with the `patch` or `patch.object` decorators.
PropertyMockprovides a way to mock property attributes on classes or objects. Here's how you can do it:
Suppose you have the following class with a property that you want to mock:
You can mock `my_property` using `PropertyMock` as follows:

new_callable argument is used with the patch.object method to specify what kind of mock object to create. When you're mocking a property with patch.object, you should use new_callable=PropertyMock. This tells patch.object to create a PropertyMock instead of the default MagicMock.Explanation
- patch.object : This line tells `unittest.mock` to replace the `my_property` property of the `MyClass` class with a `PropertyMock` object.
- mock_property.return_value: By setting the `return_value` of the `mock_property`, you're defining what the mock property should return when accessed.
- instance.my_property: When you access `my_property` on an instance of `MyClass`, it now returns the `return_value` you set on the `mock_property`, which is `'mocked value'` in this example.
This approach allows you to test code that interacts with the `my_property` property of `MyClass` instances under conditions you define through the mock, without depending on the original implementation of the property.
Notes:
MagicMock: This is the default mock object created bypatch. It's good for most cases, but it doesn't behave like a property.PropertyMock: This is a special type of mock that's designed to act like a property. Properties are attributes that you can get or set, typically with@propertydecorators in Python.
So,
new_callable=PropertyMockis needed when you want your mocked object to act like a property, not just any regular method or attribute. This way, when your test accesses the property, it will behave as if it's getting a value from a real property, thanks toPropertyMock.
Resources:
https://www.pythontutorial.net/python-unit-testing/python-patch/










