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
with
statement along withself.assertRaises(KeyError)
. This means we expect aKeyError
to be raised whenaccess_nested_map
is called with the givennested_map
andpath
.If a
KeyError
is 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 parameterized
Then, 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.get
methodreturn_value
: This is an attribute of the mock objectmock_get
that specifies what the mock should return when it is called. Sincerequests.get
returns a response object,mock_get.return_value
simulates 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_payload
What'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_value
is the fake response object thatmock_get
(our fakerequests.get
) returns when it's called..json.return_value
is the result of calling the.json()
method on this fake response. We're setting this tot_payload
, which means "when theget_json
function 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_payload
Call 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
memoization
aspect 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
memo
method is called only once when thefibo
function 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
memo
Decorator: Useunittest.mock.patch
to replace the realmemo
decorator with a mock object. This allows you to track how many times thememo
decorator is actually invoked.Call the Function: Call the
fibo
function twice with the same argument.Assert Calls: Use
assert_called_once
to verify that thememo
decorator (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.
PropertyMock
provides 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@property
decorators in Python.
So,
new_callable=PropertyMock
is 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/