patternpythongitMinor
Monkeypatching __builtin__ in tests for mocking
Viewed 0 times
mocking__builtin__testsmonkeypatchingfor
Problem
I'm testing a little magic behaviour of my script that automatically reads from
GitHub
```
def test_authentication(monkeypatch):
import os.path
import io
import __builtin__
auth_token = 'fhqwhgads\n'
# Err... Just ignore how needlessly complicated this is.
def monkeypatch_on_path_match(mod, name):
original = getattr(mod, name)
def patch(function):
def patched(path, *args, **kwargs):
if path == os.path.expanduser('~/.ghtoken'):
return function(path, *args, **kwargs)
return original(path, *args, **kwargs)
monkeypatch.setattr(mod, name, patched)
return patch
# Monkey-Patch exists() to say that the token exists.
@monkeypatch_on_path_match(os.path, 'exists')
def intercept_exists(path, *args, **kwargs):
return True
# Monkey-Patch open() to return specific file content.
@monkeypatch_on_path_match(__builtin__, 'open')
def intercept_open(path, *args, **kwargs):
return io.BytesIO(auth_token)
# Enable HTTPretty and register the index path.
httpretty.enable()
body = iter(mock_data.abbrev_search_bodies)
def request_callback(request, uri, headers):
headers['Content-Type'] = 'application/json; charset=utf-8'
headers['X-RateLimit-Remaining'] = 10
headers['Link'] = (
'; rel="last"')
return 200, headers, next(body)
httpretty.register_uri(httpretty.GET,
"https://api.github.com/search/repositories",
body=request_callback)
# Simply issue the request...
ghdwn.get_github_list('java')
assert httpretty.last_request().headers[
'Authorization'] == 'token fhqwhgads'
origin
~/.ghtoken to do automagic authentication on GitHub API requests. I want to test that this works properly by checking the headers in my mock requests to GitHub's API. I'm using HTTPretty to mock HTTP requests.GitHub
```
def test_authentication(monkeypatch):
import os.path
import io
import __builtin__
auth_token = 'fhqwhgads\n'
# Err... Just ignore how needlessly complicated this is.
def monkeypatch_on_path_match(mod, name):
original = getattr(mod, name)
def patch(function):
def patched(path, *args, **kwargs):
if path == os.path.expanduser('~/.ghtoken'):
return function(path, *args, **kwargs)
return original(path, *args, **kwargs)
monkeypatch.setattr(mod, name, patched)
return patch
# Monkey-Patch exists() to say that the token exists.
@monkeypatch_on_path_match(os.path, 'exists')
def intercept_exists(path, *args, **kwargs):
return True
# Monkey-Patch open() to return specific file content.
@monkeypatch_on_path_match(__builtin__, 'open')
def intercept_open(path, *args, **kwargs):
return io.BytesIO(auth_token)
# Enable HTTPretty and register the index path.
httpretty.enable()
body = iter(mock_data.abbrev_search_bodies)
def request_callback(request, uri, headers):
headers['Content-Type'] = 'application/json; charset=utf-8'
headers['X-RateLimit-Remaining'] = 10
headers['Link'] = (
'; rel="last"')
return 200, headers, next(body)
httpretty.register_uri(httpretty.GET,
"https://api.github.com/search/repositories",
body=request_callback)
# Simply issue the request...
ghdwn.get_github_list('java')
assert httpretty.last_request().headers[
'Authorization'] == 'token fhqwhgads'
origin
Solution
I'm assuming you're fine with
the
library.
Now for the monkey patching the other choice you have is to restructure
how the credentials are loaded. At the moment the credentials are
loaded every time a request is performed - instead of that the
credentials could be loaded once, for the whole module as globals,
before invoking any other functionality. Yes, globals are bad, but
unless you have another layer of indirection it'd be okay for this
purpose.
Done that way, you'd call e.g.
the call, then
If you stay with the current approach, I'd only move the patch method
out of the test into a proper function, as it's quite reusable, and give
it a better name maybe.
I'm not quite sure if there are other ways to open a file in Python,
regarding your concern whether you're actually testing the right thing;
it might also make sense to test two separate things: the function to
load credentials (from a custom path, so you can load from
and calling that function. Then you wouldn't mock Python standard
functions, but instead only the credential loader, while you'd test the
loading of credentials with an actual test file.
assert, generally I'd recommend usingthe
unittestlibrary.
Now for the monkey patching the other choice you have is to restructure
how the credentials are loaded. At the moment the credentials are
loaded every time a request is performed - instead of that the
credentials could be loaded once, for the whole module as globals,
before invoking any other functionality. Yes, globals are bad, but
unless you have another layer of indirection it'd be okay for this
purpose.
Done that way, you'd call e.g.
ghdwn.load_credentials(), then issuethe call, then
ghdwn.drop_credentials() and repeat.If you stay with the current approach, I'd only move the patch method
out of the test into a proper function, as it's quite reusable, and give
it a better name maybe.
I'm not quite sure if there are other ways to open a file in Python,
regarding your concern whether you're actually testing the right thing;
it might also make sense to test two separate things: the function to
load credentials (from a custom path, so you can load from
"/tmp/...")and calling that function. Then you wouldn't mock Python standard
functions, but instead only the credential loader, while you'd test the
loading of credentials with an actual test file.
Context
StackExchange Code Review Q#70045, answer score: 2
Revisions (0)
No revisions yet.