While writing unit tests in Python, there will often be times where you’ll need to fake the result of a function as testing against the actual function may be impossible. A simple example is a random function since one can’t predict what it will return. Another could be a database call for a database that’s only available in certain environments.
Python’s mock library is the de facto standard when mocking functions in Python, yet I have always struggled to understand it from the official documentation.
In this post, I’m going to focus on regular functions. We’ll take a look at mocking classes and their related properties some time in the future.
Mocking Functions Using Decorators
Let’s start with the os.urandom function. We’ll begin by writing a mock function that will act similarly to urandom:
|
|
OK, so now let’s use it!
|
|
The side_effect keyword argument simply allows you to replace an entire function with another. Please also notice that the decorator now adds an additional argument to the function that it wraps which I’ve called urandom_function. We’ll discuss some of the things you can do with this later on.
The code above also works if we were importing a function that used os.urandom too.
OK, but what if we imported the urandom function using a from statement? Well this is a special case where you can use __main__ to mock the function:
|
|
Great stuff! But in many cases, we would be importing a function from a module that calls urandom directly using a from import. For example, let’s say we had this function in a module called fots:
|
|
In this case, we can mock the urandom function in the fots module like this:
|
|
At this point, we know how to mock the various types of function calls that may occur.
If you would like to perform a much simpler mock and just replace the return value of the function with a simple expression, you may do this:
|
|
Mocking Functions Using Context Managers
For more granular control over when mocking should take place within a test case, you may use a with statement instead of a decorator as shown below.
|
|
As you can see, the syntax really doesn’t change all that much and once again you’ll have the function available within the with statement’s scope for manipulation.
Using the Mocked Function During Tests
As mentioned above, using the decorator or context manager provides access to the mocked function via an additional variable.
Firstly, we can change the mock function on the fly throughout the test like this:
|
|
We can also determine if the mock function was called and how many times it was called. These particular statistics can be reset using the reset_mock function. Please see an example below:
|
|
You may even determine exactly what parameters the mocked function was called with:
|
|
Pretty cool huh?
Conclusion
It’s easy to see how awesome this library is and why it’s now part of the standard library. Its implementation is also very Pythonic and elegant. Hopefully this little guide has gotten you over the hurdles that I first had to go through while learning it. Happy mocking! :)