Introduction to Mocking in Python
Mocking is a library for testing in Python. It allows you to replace parts of your system under test with mock objects and make assertions about how they have been used. This tutorial will discuss in detail what mocking is and how to use it in Python applications.
What Is Mocking?
Mocking is a library for testing in Python which allows you to replace parts of your system under test with mock objects and make assertions about how they have been used.
In Python, mocking is accomplished by replacing parts of your system with mock objects using the unittest.mock module. This module contains a number of useful classes and functions, namely the patch function (as decorator and context manager) and the MagicMock class. These two components are very important in achieving mocking in Python.
A mock function call usually returns a predefined value immediately. A mock object's attributes and methods are defined in the test as well, without creating the real object.
Mocking also allows you to return predefined values to each function call when writing tests. This allows you to have more control when testing.
Prerequisites
Mock is available in Python 3, but if you are using a Python version below
3.3, you can still use unittest.mock
by importing it as a separate library like so.
$ pip install mock
Benefits of Mocking
Some of the benefits of mocking include:
- Avoiding overdependence. Mocking reduces the dependence of functions. For instance, if you have a function A class that depends on a function B, you will need to write a few unit tests covering the features provided by function B. Let's say the code grows in future and you have more functions, i.e. A depends on B, B depends on C, and C depends on D. If a fault is introduced in Z, all your unit tests will fail.
- Reduced overload. This applies to resource-intensive functions. A mock of that function would cut down on unnecessary resource usage during testing, therefore reducing test run time.
- Bypass time constraints in functions. This applies to scheduled activities. Imagine a process that has been scheduled to execute every hour. In such a situation, mocking the time source lets you actually unit test such logic so that your test doesn't have to run for hours, waiting for the time to pass.
Usage
Usage of mock
is simple as:
>>> from mock import Mock >>> mock = Mock(return_values = 10) >>> mock(1,4,foo ='bar') <Mock name='mock()' id='140305284793040'> >>> mock.return_values 10
Here, we import the mock module, create a mock object, and specify return values. When the mock object is called, we want it to be able to return some values. In our case, we want the mock object to return a value of 10. If we call the mock object with the arguments (1, 4, foo ='bar')
, the result will be the value 10, which was defined as a return value.
You can also raise exceptions inside mocks as follows:
>>> mock = Mock(side_effect=KeyError('foobar')) >>> mock() Traceback (most recent call last): ... KeyError: 'foobar'
The side_effects
argument allows you to perform certain things like raising an exception when a mock is called.
Example
Consider this simple function:
import requests def api(): response = requests.get('https://www.google.com/') return response
This function performs an API request to the Google webpage and returns a response.
The corresponding simple test case will be as follows:
import unittest from main import api class TetsApi(unittest.TestCase): def test_api(self): assert api() == 200
Running the above test should give an output like so:
---------------------------------------------------------------------- Ran 1 test in 3.997s OK
Let's introduce mocking to this example, and the resulting test with the Mock module will be as shown below:
import unittest from mock import Mock from mock import patch import requests import unittest class TetsApi(unittest.TestCase): def test_api(self): with patch.object(requests, 'get') as get_mock: get_mock.return_value = mock_response = Mock() mock_response.status_code = 200 assert api() == 200
Running the above test should give an output like so:
---------------------------------------------------------------------- Ran 1 test in 0.001s OK
As seen above, the mocking module takes less time to make the same API call as the normal test case.
Larger Example
Let's assume you have a script that interacts with an external API and makes calls to that API whenever a certain function is called. In this example, we are going to use the Twitter API to implement a Python script which will post to the Twitter profile page.
We don't want to post messages on Twitter every time we test the script, and that's where Mocking comes in.
Let's get started. We will be using the python-twitter library, and the first thing we will do is create a folder python_mock
and, inside the folder, create two files, namely tweet.py
and mock_test.py
.
Write the following code to the file tweet.py
.
# pip install python-twitter import twitter # define authentication credentials consumer_key = 'iYD2sKY4NC8teRb9BUM8UguRa' consumer_secret = 'uW3tHdH6UAqlxA7yxmcr8FSMSzQIBIpcC4NNS7jrvkxREdJ15m' access_token_key = '314746354-Ucq36TRDnfGAxpOVtnK1qZxMfRKzFHFhyRqzNpTx7wZ1qHS0qycy0aNjoMDpKhcfzuLm6uAbhB2LilxZzST8w' access_token_secret = '7wZ1qHS0qycy0aNjoMDpKhcfzuLm6uAbhB2LilxZzST8w' def post_tweet(api, tweet): # post tweet status = api.PostUpdate(tweet) return status def main(): api = twitter.Api(consumer_key=consumer_key, consumer_secret=consumer_secret, access_token_key=access_token_key, access_token_secret=access_token_secret) message = raw_input("Enter your tweet :") post_tweet(api, message) if __name__ == '__main__': main()
In the code above, we first import the Twitter library and then define the authentication credentials, which you can easily get from the Twitter Apps page.
The Twitter API is exposed via the twitter.Api
class, so we create the class by passing our tokens and secret keys.
The post_tweet
function takes in an authentication object and the message and then posts the tweet to the Twitter profile.
We then go ahead and mock the API call to Twitter so that the API doesn't post to Twitter every time it is called. Go ahead and open the mock_test.py
file and add the following code.
# mock_test.py #!/usr/bin/env python import unittest from mock import Mock import tweet class TweetTest(unittest.TestCase): def test_example(self): mock_twitter = Mock() tweet.post_tweet( mock_twitter, "Creating a Task Manager App Using Ionic: Part 1") mock_twitter.PostUpdate.assert_called_with( "Creating a Task Manager App Using Ionic: Part 1") if __name__ == '__main__': unittest.main()
Running the above test should give an output like so:
---------------------------------------------------------------------- Ran 1 test in 0.001s OK
Conclusion
This tutorial has covered most of the fundamentals of mocking and how to use mocking to perform external API calls. For more information, visit the official Python mocking documentation. You can also find additional resources on authentication with the Twitter API in this tutorial.
Additionally, don’t hesitate to see what we have available for sale and for study in the Envato Market, and please go ahead and ask any questions and provide your valuable feedback using the feed below.
from Envato Tuts+ Tutorials
Comments
Post a Comment