DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • Build a Simple REST API Using Python Flask and SQLite (With Tests)
  • Leveraging LLMs for Software Testing
  • Chat Completion Models vs OpenAI Assistants API
  • Idea to Running: One Minute

Trending

  • Introduction to Retrieval Augmented Generation (RAG)
  • Intro to RAG: Foundations of Retrieval Augmented Generation, Part 1
  • The Ultimate Guide to Code Formatting: Prettier vs ESLint vs Biome
  • Agentic AI Systems: Smarter Automation With LangChain and LangGraph
  1. DZone
  2. Data Engineering
  3. Databases
  4. How to Mock a Rest API in Python

How to Mock a Rest API in Python

By 
Gunter Rotsaert user avatar
Gunter Rotsaert
DZone Core CORE ·
Apr. 16, 20 · Tutorial
Likes (6)
Comment
Save
Tweet
Share
16.4K Views

Join the DZone community and get the full member experience.

Join For Free

A few posts ago, we published a blog about how to use the Jira API. We did not write any unit tests for the application we wrote, and that is exactly what we are going to do now. More specifically, we will focus on how we can unit test a REST API.

Why Unit Tests Anyway?

Our main focus when writing software is building new features and fixing bugs. Of course, we need to test what we built, but we get the most joyful moment when our newly developed feature works. The next step is to write unit tests… But, we already know it is working, so why spend so much effort on it? It is much more fun to start with the next feature, right? So, we skip writing unit tests. But what happens when we need to extend the feature we wrote with some new functionality? 

How are we going to ensure we did not break the original feature? What if we want to refactor the code we have written? These are the moments we are glad when we notice someone took the effort to write unit tests. Unit tests will give us the confidence that we didn't break anything when we changed some code (assuming the unit tests are well written and provide enough code coverage). It is therefore also common practice to run the unit tests as part of your CI/CD pipeline. 

If you do not like writing unit tests after developing a new feature, you can also consider writing your unit tests first, letting them fail, and when you implement your code, the unit tests will pass (TDD: Test Driven Development). This way, when you have reached your uppermost moment of joy (It works!), you also have your unit tests passed (All the unit tests passed!). That doubles your moment of joy ;-) .

Create Your First Unit Test

We will build upon the sources of the Jira time report generator. We are using Python 3.7 and PyCharm as an IDE. First, let’s create a test directory and right-click the directory in PyCharm. Choose New - Python File and Python unit test. This creates the following default file:

Python
xxxxxxxxxx
1
 
1
import unittest
2
 
3
class MyTestCase(unittest.TestCase):
4
    def test_something(self):
5
        self.assertEqual(True, False)
6
 
7
 
8
if __name__ == '__main__':
9
    unittest.main()


Running this unit test obviously fails (True does not equal False), but we do have set up the basics for writing our own unit tests now.

Mocking a Rest API

We want to unit test the get_updated_issues function and this provides us a first challenge: the get_updated_issues function contains a call to the Jira Rest API. We do not want our unit test to be dependent on a third-party service, and therefore, we need a way to mock the Rest API. There are several options to mock a REST API, but we will make use of the requests-mock Python library, which fits our needs.

Install the requests-mock Python library:

Shell
xxxxxxxxxx
1
 
1
pip install requests_mock


Test Single Page Response

The get_updated_issues function will request the issues that are updated in a certain time period. In our unit test, we will verify the behavior when one page with results is retrieved (the Rest API supports pagination, but that is something for a next unit test):

Python
xxxxxxxxxx
1
15
 
1
def test_get_updated_issues_one_page(self):
2
    with open("issues_one_page.json", "r") as issues_file:
3
        mock_response = issues_file.read()
4
 
5
    expected_result = [
6
        {'expand': 'operations,versionedRepresentations,editmeta,changelog,renderedFields', 'id': '10005',
7
         'self': 'https://jira_url/rest/api/2/issue/10005', 'key': 'MYB-5'},
8
        {'expand': 'operations,versionedRepresentations,editmeta,changelog,renderedFields', 'id': '10004',
9
         'self': 'https://jira_url/rest/api/2/issue/10004', 'key': 'MYB-4'}]
10
 
11
    with requests_mock.Mocker() as m:
12
        m.register_uri('GET', '/rest/api/2/search', text=mock_response)
13
        response = jiratimereport.get_updated_issues("https://jira_url", "user_name", "api_token",  "MYB",
14
                                                     "2020-01-10", "2020-01-20", "")
15
    self.assertEqual(expected_result, response)


Let’s take a closer look at what is happening here. We have defined the Jira JSON response in file issues_one_page.json. At line 2 and 3, we read the contents of the file into variable mock_response. On line 5, we define the expected result with variable expected_result when the function get_updated_issues returns. At lines 11 to 13, the magic happens. 

We register the URI we call from within the get_updated_issues function with the Mocker we defined. The third parameter of register_uri defines the response, which should be returned from the mocked API call. At line 15, we call the get_updated_issues function, and at the end, we verify whether the response equals the expected result.

Test Paginated Response

The Jira API supports pagination. We added some functionality to the get_updated_issues function in order to handle paginated responses. The JSON response contains three fields for this:

  • startAt: indicates from which result the page should be retrieved.
  • maxResults: the number of maximum results that are returned within one response.
  • total: the total number of results.

The maxResults for retrieving issues is, at the time of writing, 50. If we would like to test this against a real Jira server, we would have to create at least 51 issues. But for testing with our unit test, we can easily change the response in order that field maxResults returns 2 for example, which makes it a lot easier to test. Our paginated unit test looks as follows:

Python
xxxxxxxxxx
1
22
 
1
def test_get_updated_issues_multiple_pages(self):
2
    with open("issues_multiple_first_page.json", "r") as issues_first_file:
3
        mock_response_first_page = issues_first_file.read()
4
 
5
    with open("issues_multiple_second_page.json", "r") as issues_second_file:
6
        mock_response_second_page = issues_second_file.read()
7
 
8
    expected_result = [
9
        {'expand': 'operations,versionedRepresentations,editmeta,changelog,renderedFields', 'id': '10005',
10
         'self': 'https://jira_url/rest/api/2/issue/10005', 'key': 'MYB-5'},
11
        {'expand': 'operations,versionedRepresentations,editmeta,changelog,renderedFields', 'id': '10004',
12
         'self': 'https://jira_url/rest/api/2/issue/10004', 'key': 'MYB-4'},
13
        {'expand': 'operations,versionedRepresentations,editmeta,changelog,renderedFields', 'id': '10006',
14
         'self': 'https://jira_url/rest/api/2/issue/10006', 'key': 'MYB-6'}]
15
 
16
    with requests_mock.Mocker() as m:
17
        m.register_uri('GET', '/rest/api/2/search', [{'text': mock_response_first_page},
18
                                                     {'text': mock_response_second_page}])
19
        response = jiratimereport.get_updated_issues("https://jira_url", "user_name", "api_token",  "MYB",
20
                                                     "2020-01-10", "2020-01-20", "")
21
 
22
    self.assertEqual(expected_result, response)


The unit test looks quite similar to the one for the single page response. We defined two mock responses this time, because the Jira API will be called twice and we want to return two different responses. The expected_result variable contains the combined result of both API calls. The real difference can be seen at line 17. Instead of defining a single mock response, we now define a list of mock responses. When the API is called more than the defined responses, the latest defined mock response is returned again.

Test Failed

The unit tests above all pass. But do they fail when something is wrong? We can therefore change the following line in the get_updated_issues function:

Python
xxxxxxxxxx
1
 
1
issues_json.extend(response_json['issues'])


with:

Python
xxxxxxxxxx
1
 
1
issues_json = response_json['issues']


This will ensure that only the response of the first API call will be added to the returned issues list, but not the response of succeeding API calls. Run both tests again. The test_get_updated_issues_one_page passes, but the test_get_updated_issues_multiple_pages fails:

Shell
xxxxxxxxxx
1
 
1
AssertionError: Lists differ


Test Multiple URI’s

The Jira work logs are to be retrieved per issue. We need to register more than one URI with different responses for each issue. The response of the get_work_logs function returns a list of WorkLog objects which we can assert.

Python
x
22
 
1
def test_get_work_logs_one_page(self):
2
    with open("work_logs_first_issue_one_page.json", "r") as first_issue_file:
3
        mock_response_first_issue = first_issue_file.read()
4
 
5
    with open("work_logs_second_issue_one_page.json", "r") as second_issue_file:
6
        mock_response_second_issue = second_issue_file.read()
7
 
8
    issues_json = [
9
        {'expand': 'operations,versionedRepresentations,editmeta,changelog,renderedFields', 'id': '10005',
10
         'self': 'https://jira_url/rest/api/2/issue/10005', 'key': 'MYB-5'},
11
        {'expand': 'operations,versionedRepresentations,editmeta,changelog,renderedFields', 'id': '10004',
12
         'self': 'https://jira_url/rest/api/2/issue/10004', 'key': 'MYB-4'}]
13
 
14
    with requests_mock.Mocker() as m:
15
        m.register_uri('GET', '/rest/api/2/issue/MYB-5/worklog/', text=mock_response_first_issue)
16
        m.register_uri('GET', '/rest/api/2/issue/MYB-4/worklog/', text=mock_response_second_issue)
17
        work_logs = jiratimereport.get_work_logs("https://jira_url", "user_name", "api_token",
18
                                                 "2020-01-10", "2020-01-20", "", issues_json)
19
 
20
    self.assertEqual(work_logs[0], WorkLog("MYB-5", datetime(2020, 1, 18), 3600, "John Doe"))
21
    self.assertEqual(work_logs[1], WorkLog("MYB-5", datetime(2020, 1, 18), 5400, "John Doe"))
22
    self.assertEqual(work_logs[2], WorkLog("MYB-4", datetime(2020, 1, 12), 3600, "John Doe"))


Conclusion

Writing unit tests is absolutely necessary when you want to develop software in a professional manner. In this post, we took a look at how to unit test a Rest API by means of the requests-mock Python library. We only scratched the surface of what this library has to offer, but our first impressions are very good.

API unit test Python (language)

Published at DZone with permission of Gunter Rotsaert, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Build a Simple REST API Using Python Flask and SQLite (With Tests)
  • Leveraging LLMs for Software Testing
  • Chat Completion Models vs OpenAI Assistants API
  • Idea to Running: One Minute

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!