Skip to content

Commit

Permalink
Added httpretty.register decorator
Browse files Browse the repository at this point in the history
  • Loading branch information
syndbg committed Mar 14, 2015
1 parent df02999 commit 254444d
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 72 deletions.
93 changes: 69 additions & 24 deletions httpretty/core.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# #!/usr/bin/env python
# !/usr/bin/env python
# -*- coding: utf-8 -*-
# <HTTPretty - HTTP client mock for Python>
# Copyright (C) <2011-2013> Gabriel Falcão <[email protected]>
Expand Down Expand Up @@ -107,6 +107,7 @@


class HTTPrettyRequest(BaseHTTPRequestHandler, BaseClass):

"""Represents a HTTP request. It takes a valid multi-line, `\r\n`
separated string with HTTP headers and parse them out using the
internal `parse_request` method.
Expand Down Expand Up @@ -138,6 +139,7 @@ class HTTPrettyRequest(BaseHTTPRequestHandler, BaseClass):
`content-type` headers values: 'application/json' or
'application/x-www-form-urlencoded'
"""

def __init__(self, headers, body=''):
# first of all, lets make sure that if headers or body are
# unicode strings, it must be converted into a utf-8 encoded
Expand Down Expand Up @@ -229,12 +231,14 @@ class HTTPrettyRequestEmpty(object):


class FakeSockFile(StringIO):

def close(self):
self.socket.close()
StringIO.close(self)


class FakeSSLSocket(object):

def __init__(self, sock, *args, **kw):
self._httpretty_sock = sock

Expand All @@ -243,6 +247,7 @@ def __getattr__(self, attr):


class fakesock(object):

class socket(object):
_entry = None
debuglevel = 0
Expand Down Expand Up @@ -380,7 +385,8 @@ def sendall(self, data, *args, **kw):
is_parsing_headers = False

if not self._entry:
# If the previous request wasn't mocked, don't mock the subsequent sending of data
# If the previous request wasn't mocked, don't mock the subsequent sending
# of data
return self.real_sendall(data, *args, **kw)

self.fd.seek(0)
Expand Down Expand Up @@ -492,6 +498,7 @@ def fake_getaddrinfo(


class Entry(BaseClass):

def __init__(self, method, uri, body,
adding_headers=None,
forcing_headers=None,
Expand Down Expand Up @@ -543,15 +550,15 @@ def validate(self):
igot = int(got)
except ValueError:
warnings.warn(
'HTTPretty got to register the Content-Length header ' \
'HTTPretty got to register the Content-Length header '
'with "%r" which is not a number' % got,
)

if igot > self.body_length:
raise HTTPrettyError(
'HTTPretty got inconsistent parameters. The header ' \
'Content-Length you registered expects size "%d" but ' \
'the body you registered for that has actually length ' \
'HTTPretty got inconsistent parameters. The header '
'Content-Length you registered expects size "%d" but '
'the body you registered for that has actually length '
'"%d".' % (
igot, self.body_length,
)
Expand Down Expand Up @@ -588,7 +595,8 @@ def fill_filekind(self, fk):
headers = self.normalize_headers(headers)
status = headers.get('status', self.status)
if self.body_is_callable:
status, headers, self.body = self.callable_body(self.request, self.info.full_url(), headers)
status, headers, self.body = self.callable_body(
self.request, self.info.full_url(), headers)
headers.update({
'content-length': len(self.body)
})
Expand Down Expand Up @@ -640,6 +648,7 @@ def url_fix(s, charset='utf-8'):


class URIInfo(BaseClass):

def __init__(self,
username='',
password='',
Expand Down Expand Up @@ -763,7 +772,7 @@ def __init__(self, uri, entries, match_querystring=False):

self.entries = entries

#hash of current_entry pointers, per method.
# hash of current_entry pointers, per method.
self.current_entries = {}

def matches(self, info):
Expand All @@ -787,7 +796,7 @@ def get_next_entry(self, method, info, request):
if method not in self.current_entries:
self.current_entries[method] = 0

#restrict selection to entries that match the requested method
# restrict selection to entries that match the requested method
entries_for_method = [e for e in self.entries if e.method == method]

if self.current_entries[method] >= len(entries_for_method):
Expand Down Expand Up @@ -818,6 +827,7 @@ def __eq__(self, other):


class httpretty(HttpBaseClass):

"""The URI registration class"""
_entries = {}
latest_requests = []
Expand All @@ -840,13 +850,14 @@ def record(cls, filename, indentation=4, encoding='utf-8'):
try:
import urllib3
except ImportError:
raise RuntimeError('HTTPretty requires urllib3 installed for recording actual requests.')

raise RuntimeError(
'HTTPretty requires urllib3 installed for recording actual requests.')

http = urllib3.PoolManager()

cls.enable()
calls = []

def record_request(request, uri, headers):
cls.disable()

Expand Down Expand Up @@ -885,7 +896,8 @@ def playback(cls, origin):
for item in data:
uri = item['request']['uri']
method = item['request']['method']
cls.register_uri(method, uri, body=item['response']['body'], forcing_headers=item['response']['headers'])
cls.register_uri(
method, uri, body=item['response']['body'], forcing_headers=item['response']['headers'])

yield
cls.disable()
Expand Down Expand Up @@ -1034,19 +1046,27 @@ def enable(cls):
ssl.__dict__['sslwrap_simple'] = fake_wrap_socket


def httprettified(test):
"A decorator tests that use HTTPretty"
def decorate_class(klass):
for attr in dir(klass):
if not attr.startswith('test_'):
continue
def decorate_class(klass, callable_fn):
"""
A helper method to apply callable_fn to class attributes.
It's not intended for direct use.
"""
for attr in dir(klass):
if not attr.startswith('test_'):
continue

attr_value = getattr(klass, attr)
if not hasattr(attr_value, "__call__"):
continue

setattr(klass, attr, callable_fn(attr_value))
return klass

attr_value = getattr(klass, attr)
if not hasattr(attr_value, "__call__"):
continue

setattr(klass, attr, decorate_callable(attr_value))
return klass
def httprettified(test):
"""
A decorator that activates HTTPretty.
"""

def decorate_callable(test):
@functools.wraps(test)
Expand All @@ -1060,5 +1080,30 @@ def wrapper(*args, **kw):
return wrapper

if isinstance(test, ClassTypes):
return decorate_class(test)
return decorate_class(test, decorate_callable)
return decorate_callable(test)


def register(**dec_kwargs):
"""
A decorator that activates HTTPretty and registers an uri path,
by given keyword arguments.
"""

def decorator(func):
def decorate_callable(func):
@functools.wraps(func)
def wrapper(*args, **kw):
httpretty.reset()
httpretty.enable()
httpretty.register_uri(**dec_kwargs)
try:
return func(*args, **kw)
finally:
httpretty.disable()
return wrapper

if isinstance(func, ClassTypes):
return decorate_class(func, decorate_callable)
return decorate_callable(func)
return decorator
48 changes: 0 additions & 48 deletions tests/functional/test_decorator.py

This file was deleted.

86 changes: 86 additions & 0 deletions tests/functional/test_decorators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# coding: utf-8
from unittest import TestCase
from sure import expect
from httpretty import httprettified, HTTPretty
from httpretty.core import register

try:
import urllib.request as urllib2
except ImportError:
import urllib2


@httprettified
def test_httprettified_decorator():
HTTPretty.register_uri(
HTTPretty.GET, 'http://localhost/',
body='glub glub')

fd = urllib2.urlopen('http://localhost/')
contents = fd.read()
fd.close()
expect(contents).to.equal(b'glub glub')


@httprettified
class HTTPrettifiedClass(TestCase):

def setUp(self):
self.assertFalse(HTTPretty.is_enabled())

def tearDown(self):
self.assertFalse(HTTPretty.is_enabled())

def test_decorated(self):
HTTPretty.register_uri(
HTTPretty.GET, 'http://localhost/',
body='glub glub')

fd = urllib2.urlopen('http://localhost/')
contents = fd.read()
fd.close()

expect(contents).to.equal(b'glub glub')

def test_decorated2(self):
HTTPretty.register_uri(
HTTPretty.GET, 'http://localhost/',
body='buble buble')

fd = urllib2.urlopen('http://localhost/')
contents = fd.read()
fd.close()

expect(contents).to.equal(b'buble buble')


@register(method=HTTPretty.GET, uri='http://localhost/', body='glub glub')
def test_register_uri_decorator():
fd = urllib2.urlopen('http://localhost/')
contents = fd.read()
fd.close()
expect(contents).to.equal(b'glub glub')


@register(method=HTTPretty.GET, uri='http://localhost/', body='bubble pop')
class HTTPregisterClass(TestCase):

def setUp(self):
self.assertFalse(HTTPretty.is_enabled())

def tearDown(self):
self.assertFalse(HTTPretty.is_enabled())

def test_decorated(self):
fd = urllib2.urlopen('http://localhost/')
contents = fd.read()
fd.close()

expect(contents).to.equal(b'bubble pop')

def test_decorated2(self):
fd = urllib2.urlopen('http://localhost/')
contents = fd.read()
fd.close()

expect(contents).to.equal(b'bubble pop')

0 comments on commit 254444d

Please sign in to comment.