Skip to content

Instantly share code, notes, and snippets.

@edthrn
Last active October 19, 2023 05:09
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save edthrn/ce28cb1a8a86caab0129a302d8c2bc33 to your computer and use it in GitHub Desktop.
Save edthrn/ce28cb1a8a86caab0129a302d8c2bc33 to your computer and use it in GitHub Desktop.
Unit-testing with PyGithub

Intro

Lately, I've been building a Python platform that relies heavily on interactions with Github. Instead of re-inventing the wheel, I decided to go with PyGithub as a wrapper around Github v3 API.

The problem

This library implements the full API, which allows me to be very productive when I need to add a workflow in my platform that involves Github. However, it quickly became a real PITA to write unit tests. Even if the package comes with its own testing Framework, it is not documented yet and I didn't manage to crack it up in a decent amount of time.

I decided to hack the testing a little bit, using another very cool package, httpretty. Httpretty allows you to monkey patch the socket module during testing, so you can respond anything you want to any kind of network requests. Here's what I came up with, do not hesitate to give any feedback.


NOTE This is a workaround solution, at best, knowing that PyGithub does have a built-in testing framework. If, like me and a few others, you don't want to spend too much time trying to figure out how it works, my solution is quick, easy and efficient enough... But is probably not as good as the built-in solution ;-)

{
"login": "octocat",
"id": 1,
"node_id": "MDQ6VXNlcjE=",
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
"gravatar_id": "",
"url": "https://api.github.com/users/octocat",
"html_url": "https://github.com/octocat",
"followers_url": "https://api.github.com/users/octocat/followers",
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
"organizations_url": "https://api.github.com/users/octocat/orgs",
"repos_url": "https://api.github.com/users/octocat/repos",
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
"received_events_url": "https://api.github.com/users/octocat/received_events",
"type": "User",
"site_admin": false,
"name": "monalisa octocat",
"company": "GitHub",
"blog": "https://github.com/blog",
"location": "San Francisco",
"email": "octocat@github.com",
"hireable": false,
"bio": "There once was...",
"public_repos": 2,
"public_gists": 1,
"followers": 20,
"following": 0,
"created_at": "2008-01-14T04:33:35Z",
"updated_at": "2008-01-14T04:33:35Z"
}
from unittest import TestCase
import httpretty
class JsonContent:
"""Descriptor that sets a new class attribute based on the JSON file of the same name"""
def __init__(self, name):
self.name = name
def __get__(self, instance, owner):
if instance is None:
return owner
with open(f'your/path/{self.name}.json') as file:
setattr(instance, self.name, file.read())
return getattr(instance, self.name)
class MockGithubResponse:
user = JsonContent('user')
# add any Github object you need here
class PyGithubTestCase(TestCase):
def setUp(self):
httpretty.enable()
httpretty.reset()
base_url = 'https://api.github.com'
headers = {
'content-type': 'application/json',
'X-OAuth-Scopes': 'admin:org, admin:repo_hook, repo, user',
'X-Accepted-OAuth-Scopes': 'repo'
}
fake = MockGithubResponse()
response_mapping = {
'/user(/(\w+))?': fake.user,
# Add API url RegEx, with its corresponding response here...
}
for url, response in response_mapping.items():
# Note: Here, I only bind `GET` methods, but you can bind any method you want
httpretty.register_uri(
httpretty.GET,
re.compile(base_url + url),
response,
adding_headers=headers # You need this!
)
def tearDown(self):
httpretty.disable()
import json
from github import Github
from .base import PyGithubTestCase
class TestUser(PyGithubTestCase):
def test_username(self):
g = Github('fake-token')
user = g.get_user()
with open('path/to/user.json', 'rb') as f:
expected_body = json.load(f)
self.assertEqual(user.login, expected_body['login']) # True
@edthrn
Copy link
Author

edthrn commented Aug 18, 2020

This error is probably saying that the monkey-patching of your socket connection did not work for some reason. I suggest you double-check your configuration with httpretty

Also, note that this is not how tests should be invoked in Python. You may want to have a look at unittest and/or pytest

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment