How to Mock Python Import

Abid Ullah Feb 02, 2024
  1. Python Mock Import
  2. Common Pitfalls of Mocking in Python
  3. Basic Usage of Mock in Python
  4. How to Use Python Mock Import
  5. Python Dependencies in Testing
  6. Python mock Objects
How to Mock Python Import

In this Python article, we will examine the mock library and learn how to use it effectively. We will start with simple examples and then look at more advanced uses.

We will learn the uses and pitfalls of mock objects and mocking.

Python Mock Import

Python’s mock library is one of the most popular libraries for unit testing. It allows us to replace parts of our system with mock objects and assert that they are used as expected.

Mocking is a powerful tool, but it’s often misunderstood. This article will discuss what mocking is, how it can be used, and some common pitfalls.

Mocking in Python

Mocking is the process of replacing a real object with a fake object. The fake object is called a mock.

Mocking allows us to test how our code interacts with other parts of our system without actually depending on those other parts. For example, we can mock a database to test how our code interacts with it without having a database.

How to Use Mock in Python

Mocking can be used for two different purposes:

  1. To test the behavior of our code

    For example, you can mock a database to assert that our code is querying it correctly.

  2. To stub out behavior that we don’t want to test

    For example, we might mock a database to avoid connecting to it.

Which purpose we are using mocking for will determine how we use it.

Testing Behavior

When we use mocking to test behavior, we want to assert that our code interacts with the mock object as expected. The mock object should have the same interface as the real object so that our code doesn’t know it’s a mock.

We can use the assert_called_with() method to assert that a mock method was called with the expected arguments. For example, if we’re mocking a database, we might assert that the query() method was called with the correct SQL.

We can also use the assert_called() method to assert that a mock method was called. This is useful when we don’t care about the arguments or when the arguments are complex and difficult to assert.

Stubbing Out Behavior

When we use mocking to stub out behavior, we want to configure the mock object to return the values you expect. For example, if we are mocking a database, we might configure the query() method to return a list of dummy data.

We can use the side_effect attribute to configure a mock object. The side_effect can be any value, including a function.

When the mock is called, the side_effect is returned.

For example, we can use a side_effect to return different values each time a mock is called. This is useful for simulating errors or different behaviors.

We can also use the side_effect to raise an exception. This is useful for simulating errors.

Common Pitfalls of Mocking in Python

There are some common pitfalls when using mocking:

  1. One pitfall is trying to mock too much. Mocking is a powerful tool, but it’s not a panacea.

    Mocking is most useful when we are testing the behavior of our code, not the behavior of our whole system.

    If we try to mock too much, we will end up with many mock objects, and our tests will be hard to maintain. Mocking only the needed objects and using real objects for the rest is better.

  2. Another pitfall is using mocking when it’s not appropriate. Mocking is most useful when you’re testing isolated parts of your code.

    If you’re testing the whole system, it’s usually better to use integration tests. Integration tests are tests that exercise the whole system, and they’re slower and more expensive to run but more accurate.

  3. Finally, a common mistake is to use mocks when we should be using fakes. A fake object is an object that imitates a real object but doesn’t have the same interface.

For example, a fake database might return hard-coded data instead of connecting to a real database. Fakes help stub out behavior, but they’re not as useful for testing behavior.

Basic Usage of Mock in Python

The most basic use of mock is to replace an object with a mock object.

For example, let’s say you have a function that takes an object as an argument and does something with it. Maybe it prints the object’s name.

Example code:

def print_name(obj):
    print(obj.name)

If we wanted to test this function, we could create an object with a name attribute and pass it into the function.

Example code:

class TestObject:
    def __init__(self, name):
        self.name = name


obj = TestObject("Abid")
print_name(obj)

Output:

Abid

The code works, and we can see the name printed as Abid. But it has a few drawbacks.

First, it requires us to create a real object just for testing. This might not be a big deal in this simple example, but in a more complex system, it can be a lot of work to set up the necessary objects just for testing.

Second, this approach is not very flexible. To test different behaviors, we must create a new real object each time.

For example, what if we want to test what happens when the object doesn’t have a name attribute?

Example code:

class TestObject:
    def __init__(self, name):
        self.name = name


obj = TestObject()
print_name(obj)

With the real object, we would get an error. But using the test object, the output of the code will be what is missing in the code.

Output:

__init__() missing 1 required positional argument: 'name'

How to Use Python Mock Import

To work with a mock import for testing purposes, let’s create and explore a mock object.

First, we need to import the mock library. The mock library will give us the mock class from which we can make our mock objects.

After importing the library, we will call our mock class and print it to see what this mock object looks like.

Example code:

from unittest.mock import Mock

mock = Mock()
print(mock)

Output:

<Mock id='139754809551888'>

The output of the code shows a representation of the mock object, with the ID id='139754809551888' string of numbers.

Now let’s explore what we can do with this mock object and how we can use it. Well, mock objects are generally used to patch other objects in our code.

When we say patch, it means to replace, imitate or mock. They are all the same.

Remember, the essence of mocking is to replace something as close to the real thing as possible. Also, remember that when running our mock test, we want it to be in a controlled environment.

So, external decencies don’t make our mock test fail.

Let’s say we have an external dependency in our code. Let’s use json as an example of external dependency.

And it’s hard to control and has behavior that we don’t necessarily want to happen in our test.

What we can do is use our mock object to patch this dependency. So, first of all, we need to import the json file format.

Then we will use JSON’s json.dumps method and associate a dictionary with it. So we are just using a method from the external dependency method.

Then we will patch JSON using json = mock, and the mock object does not have any dumbs object. To prove this, we need to print the directory of json.

Example code:

import json

data = json.dumps({"a": 1})
json = mock
print(dir(json))

Output:

['assert_any_call', 'assert_called', 'assert_called_once', 'assert_called_once_with', 'assert_called_with', 'assert_has_calls', 'assert_not_called', 'attach_mock', 'call_args', 'call_args_list', 'call_count', 'called', 'configure_mock', 'dumps', 'getdoc', 'method_calls', 'mock_add_spec', 'mock_calls', 'reset_mock', 'return_value', 'side_effect']

We can see strings of objects and methods as output associated with the mock object. And notice that dumps is not one of the methods in the code output.

Python Dependencies in Testing

If a function that we are writing a unit test for uses any dependencies, such as a request module or date time. Then there is a chance that our unit test will not always get the same output from the function that is being tested.

Let’s say we have a function that uses the request library and makes some HTTP requests. So now, if we run our unit test without an internet connection, it will fail and get a connection error from the request library.

That’s why it is better to test our code in a controlled environment to gain control over unpredictable dependencies, where we can replace the actual call to a dependency with a mock object. That will allow us to tweak the behavior of that dependency.

For example, instead of making an actual HTTP request, we can provide some dummy HTTP response which will be returned on the calling request.get() function.

Dependencies are also spoofed because a mock object has information about its users that can be looked at.

Such as, if we have called, how many times did we call, or how many times have we called a particular dependency? So this can help us write our more robust unit tests.

So, we will now explore how to mock objects in Python for testing.

Python mock Objects

We will work on two Python scripts to better understand Python mock objects.

  1. roll_dice_function
  2. mock roll_dice_function

So we will work on a code that will generate random values. It will have a simple roll_dice_function that returns an integer between a number and another.

And we will provide both numbers to our function, roll_dice_function.

Example code:

# import library
import random

# define function


def roll_dice_function():
    print("The number is....")
    return random.randint(5, 10)

So we import the library first. Then we define our function using the def keyword.

This function returns an integer between 5 and 10. It uses the random.randint function from the random module.

Once we are done with the code of roll_dice_function, we will write another code using the mock object to test the roll_dice_function. Let’s go ahead.

First, we need to import the testing library for mock.

# import library
from unittest.mock import Mock
import random

# define function


def roll_dice_function():
    print("The number is....")
    return random.randint(5, 10)

Let’s see how it works. Now we will consider the functionality of the roll_dice_function we created earlier.

So, whenever we call the function, we get a random number from 5 to 10.

Example code:

# import library
from unittest.mock import Mock
import random

# define function


def roll_dice_function():
    print("The number is....")
    return random.randint(5, 10)


roll_dice_function()

Output:

The number is....
7

Let’s run the code again and see what number we get this time.

Output:

The number is....
5

So, every time we call the function, we get a random number generated between 5 and 10.

Now, we will use mock to make the function return the same value every time we call it.

Create mock Object

To create a mock object, we must create a mock class object defined in the mock library.

Example code:

mock_roll_object = mock.Mock()

Now we will call the mock object mock_roll_object.

mock_roll_object = mock.Mock()
mock_roll_object

Output:

<Mock name='mock.Mock()' id='139883130268416'>

It shows that when we call the mock object, we will get the output as a mock object. So, this is the thing about mock objects in general; whenever we call for mock objects, we get a mock object as an output.

To simplify, we can define a few things in the mock object. For debugging purposes, we can define the name.

Example code:

mock_roll_object = mock.Mock(name="mocking roll dice")

So, when we try to debug our unit test, that name might appear, which can help us debug.

Output:

<Mock name='mocking roll dice' id='139883130347136'>

The name of our mock is mocking roll dice here.

Now, if we want to return a particular value, we will have to assign the value to return_value. So, we will get the same number whenever we call the mock object.

Example code:

mock_roll_object = mock.Mock(name="mocking roll dice", return_value=8)

Now we will call the object.

mock_roll_object()

Output:

8

We will get 8 every time we call the mock object. Because we have assigned 8 to return_value, we will get the same return value.

We hope that you find this Python article helpful in understanding how to use Mock in Python.

Author: Abid Ullah
Abid Ullah avatar Abid Ullah avatar

My name is Abid Ullah, and I am a software engineer. I love writing articles on programming, and my favorite topics are Python, PHP, JavaScript, and Linux. I tend to provide solutions to people in programming problems through my articles. I believe that I can bring a lot to you with my skills, experience, and qualification in technical writing.

LinkedIn

Related Article - Python Mock