1

My code tries to make HTTP requests to Gitlab API to make different actions such as create projects, branches, milestones etc. I'm not using external modules like requests because I want to keep my project free of dependencies and I haven't found the need to import any so far. That said I'm trying to assert what my HTTPS Connection requests with a mock in my tests in this way:

gitlab_requests_tests.py

def test_project_creation(self):
        connection = http.client.HTTPSConnection("gitlab.com")
        r = urllib.request.Request(
            method="POST",
            url="https://gitlab.com/api/v4/projects",
            headers={
                "PRIVATE-TOKEN": os.environ.get("ACCESS_TOKEN"),
                "name": Path.cwd().name
            },
        )
        glab_requests.create_project(connection)
        with patch("http.client.HTTPSConnection.request") as https_mock:
            https_mock.assert_called_with(r)

Which tests this code:

gitlab_requests.py

def create_project(connection: http.client.HTTPSConnection):
    header={
        "PRIVATE-TOKEN": os.getenv("ACCESS_TOKEN", default="*"),
        "name": Path.cwd().name
    }
    if re.search(r"[^\w-]", os.getenv("ACCESS_TOKEN", default="*")):
        raise GlabRequestException("Invalid Access token format")
    connection.request("POST", "/api/v4/projects", headers=header)

I know that I asserting my request with url.request.Request isn't the right way to do because it creates a different request object to the one I'm calling from my source code.

How can I assert my request? What am I missing/doing wrong?

2 Answers 2

1

I have done following changes to your code:

  1. many changes to your test code (see directly the code and the comments added)
  2. added the custom class Exception: GlabRequestException only to execute the test
  3. added some imports to the file gitlab_requests.py

File gitlab_requests_tests.py

import unittest
from unittest.mock import patch
import http.client
import gitlab_requests as glab_requests
import os
from pathlib import Path

class MyTestCase(unittest.TestCase):

    def test_project_creation(self):
        # I have added this decorator for set the return_value of the function getenv()
        with patch('gitlab_requests.os.getenv') as mock_getenv:
            # return something that not raise a GlabRequestException
            mock_getenv.return_value = 'access_token'
            # the following patch instruction defines the mock object
            # to pass to create_project() that is the code under test
            with patch("http.client.HTTPSConnection") as https_connection_mock:
                glab_requests.create_project(https_connection_mock)
                https_connection_mock.request.assert_called_with("POST", "/api/v4/projects", headers={
                    "PRIVATE-TOKEN": os.getenv("ACCESS_TOKEN", default="*"),
                    "name": Path.cwd().name
                })

if __name__ == '__main__':
    unittest.main()

File gitlab_requests.py

import http.client
import re
import os
from pathlib import Path

class GlabRequestException(Exception):
    pass

def create_project(connection: http.client.HTTPSConnection):
    header={
        "PRIVATE-TOKEN": os.getenv("ACCESS_TOKEN", default="*"),
        "name": Path.cwd().name
    }
    if re.search(r"[^\w-]", os.getenv("ACCESS_TOKEN", default="*")):
        raise GlabRequestException("Invalid Access token format")
    connection.request("POST", "/api/v4/projects", headers=header)

Output of the execution on my system

.
----------------------------------------------------------------------
Ran 1 test in 0.001s

OK
Sign up to request clarification or add additional context in comments.

Comments

0

My first mistake was to call glab_requests.create_project(connection) outside my mock with keyboard. I was used to use decorators in other projects and made the called in the wrong place this time. My second mistake was to try to assert a new and different request to the one I was making from my source code. The assertion https_mock.assert_called_with() had to take hard coded parameters to be compared to the called made from source code.

def test_project_creation(self):
        connection = http.client.HTTPSConnection("gitlab.com")
        with patch("http.client.HTTPSConnection.request") as https_mock:
            glab_requests.create_project(connection)
            https_mock.assert_called_once_with(
                "POST",
                "/api/v4/projects",
                headers={
                    "PRIVATE-TOKEN": os.environ.get("ACCESS_TOKEN"),
                    "name": Path.cwd().name,
                },
            )

4 Comments

I have posted an answer at the same time you have posted your answer :). My answer is not so far from yours, but are you sure that if you create the object connection and pass it to create_project() the test is it passed?
I agree with this your sentence: The assertion https_mock.assert_called_with() had to take hard coded parameters to be compared to the called made from source code.
Thanks for contributing to my question @User051209! I set the environment variable ACCESS_TOKEN for each test in my suite with setUp(). It was cool to see how you avoided the exception though. Regarding, creating the connection object, I planned of creating one externally but I spent some time considering and I still have some doubts if I should better just declare it inside each function I'm working on. Good point though, thanks for mention it \m/.
I'm gonna accept your answer because it handles the exception in the test and that could be more clear to other devs who want to take a look at this question. Cheers!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.