version 0.2, recording the requests
This commit is contained in:
86
README.md
86
README.md
@@ -28,15 +28,19 @@ are supposed to get mocked.
|
||||
|
||||
# Usage
|
||||
|
||||
from httpretty import HTTPretty
|
||||
HTTPretty.register_uri(HTTPretty.GET, "http://globo.com/",
|
||||
body="The biggest portal in Brazil")
|
||||
## expecting a simple response body
|
||||
|
||||
fd = urllib2.urlopen('http://globo.com')
|
||||
got = fd.read()
|
||||
fd.close()
|
||||
```python
|
||||
from httpretty import HTTPretty
|
||||
HTTPretty.register_uri(HTTPretty.GET, "http://globo.com/",
|
||||
body="The biggest portal in Brazil")
|
||||
|
||||
print got
|
||||
fd = urllib2.urlopen('http://globo.com')
|
||||
got = fd.read()
|
||||
fd.close()
|
||||
|
||||
print got
|
||||
```
|
||||
|
||||
**:: output ::**
|
||||
|
||||
@@ -44,39 +48,63 @@ are supposed to get mocked.
|
||||
|
||||
## rotating responses
|
||||
|
||||
HTTPretty.register_uri(HTTPretty.GET, "http://github.com/gabrielfalcao/httpretty",
|
||||
responses=[
|
||||
HTTPretty.Response(body="first response", status=201),
|
||||
HTTPretty.Response(body='second and last response', status=202),
|
||||
])
|
||||
same URL, same request method, the first request return the first
|
||||
HTTPretty.Response, all the subsequent ones return the last (status
|
||||
202)
|
||||
|
||||
request1 = urllib2.urlopen('http://github.com/gabrielfalcao/httpretty')
|
||||
body1 = request1.read()
|
||||
request1.close()
|
||||
```python
|
||||
HTTPretty.register_uri(HTTPretty.GET, "http://github.com/gabrielfalcao/httpretty",
|
||||
responses=[
|
||||
HTTPretty.Response(body="first response", status=201),
|
||||
HTTPretty.Response(body='second and last response', status=202),
|
||||
])
|
||||
|
||||
assert that(request1.code).equals(201)
|
||||
assert that(body1).equals('first response')
|
||||
request1 = urllib2.urlopen('http://github.com/gabrielfalcao/httpretty')
|
||||
body1 = request1.read()
|
||||
request1.close()
|
||||
|
||||
request2 = urllib2.urlopen('http://github.com/gabrielfalcao/httpretty')
|
||||
body2 = request2.read()
|
||||
request2.close()
|
||||
assert that(request2.code).equals(202)
|
||||
assert that(body2).equals('second and last response')
|
||||
assert that(request1.code).equals(201)
|
||||
assert that(body1).equals('first response')
|
||||
|
||||
request3 = urllib2.urlopen('http://github.com/gabrielfalcao/httpretty')
|
||||
body3 = request3.read()
|
||||
request3.close()
|
||||
assert that(request3.code).equals(202)
|
||||
assert that(body3).equals('second and last response')
|
||||
request2 = urllib2.urlopen('http://github.com/gabrielfalcao/httpretty')
|
||||
body2 = request2.read()
|
||||
request2.close()
|
||||
assert that(request2.code).equals(202)
|
||||
assert that(body2).equals('second and last response')
|
||||
|
||||
request3 = urllib2.urlopen('http://github.com/gabrielfalcao/httpretty')
|
||||
body3 = request3.read()
|
||||
request3.close()
|
||||
assert that(request3.code).equals(202)
|
||||
assert that(body3).equals('second and last response')
|
||||
```
|
||||
|
||||
# Documentation
|
||||
|
||||
Unfortunately HTTPretty is lacking a documentation, but as for it is 100% based on [FakeWeb](http://fakeweb.rubyforge.org/), a good way to learn it is by looking at **HTTPretty** tests right [here](http://github.com/gabrielfalcao/HTTPretty/blob/master/tests/functional/test_urllib2.py)
|
||||
## expect for a response, and check the request got by the "server" to make sure it was fine.
|
||||
|
||||
```python
|
||||
from httpretty import HTTPretty
|
||||
from httplib2 import Http
|
||||
|
||||
HTTPretty.register_uri(HTTPretty.PATCH, "http://api.github.com/",
|
||||
body='{"repositories": ["HTTPretty", "lettuce"]}')
|
||||
|
||||
client = Http()
|
||||
headers, body = client.request('http://api.github.com', 'PATCH',
|
||||
body='{"username": "gabrielfalcao"}',
|
||||
headers={
|
||||
'content-type': 'text/json',
|
||||
})
|
||||
assert body == '{"repositories": ["HTTPretty", "lettuce"]}'
|
||||
assert HTTPretty.last_request.method == 'PATCH'
|
||||
assert HTTPretty.last_request.headers['content-type'] == 'text/json'
|
||||
```
|
||||
|
||||
# Dependencies
|
||||
|
||||
you will need **ONLY** if you decide to contribute to HTTPretty which means you're gonna need run our test suite
|
||||
you will need **ONLY** if you decide to contribute to HTTPretty which
|
||||
means you're gonna need run our test suite
|
||||
|
||||
* [nose](http://code.google.com/p/python-nose/)
|
||||
* [sure](http://github.com/gabrielfalcao/sure/)
|
||||
|
||||
@@ -23,16 +23,20 @@
|
||||
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
# OTHER DEALINGS IN THE SOFTWARE.
|
||||
version = '0.1'
|
||||
version = '0.2'
|
||||
|
||||
import re
|
||||
import socket
|
||||
import warnings
|
||||
import logging
|
||||
import traceback
|
||||
|
||||
from datetime import datetime
|
||||
from StringIO import StringIO
|
||||
from urlparse import urlsplit
|
||||
|
||||
from BaseHTTPServer import BaseHTTPRequestHandler
|
||||
|
||||
old_socket = socket.socket
|
||||
old_create_connection = socket.create_connection
|
||||
old_gethostbyname = socket.gethostbyname
|
||||
@@ -51,6 +55,24 @@ class HTTPrettyError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class HTTPrettyRequest(BaseHTTPRequestHandler):
|
||||
def __init__(self, headers, body=''):
|
||||
self.body = body
|
||||
self.raw_headers = headers
|
||||
|
||||
self.rfile = StringIO(headers + body)
|
||||
self.raw_requestline = self.rfile.readline()
|
||||
self.error_code = self.error_message = None
|
||||
self.parse_request()
|
||||
self.method = self.command
|
||||
|
||||
def __repr__(self):
|
||||
return 'HTTPrettyRequest(headers={0}, body="{1}")'.format(
|
||||
self.headers,
|
||||
self.body,
|
||||
)
|
||||
|
||||
|
||||
class FakeSockFile(StringIO):
|
||||
def read(self, amount=None):
|
||||
amount = amount or self.len
|
||||
@@ -71,7 +93,7 @@ class fakesock(object):
|
||||
class socket(object):
|
||||
_entry = None
|
||||
debuglevel = 0
|
||||
|
||||
_sent_data = []
|
||||
def __init__(self, family, type, protocol):
|
||||
self.family = family
|
||||
self.type = type
|
||||
@@ -113,15 +135,34 @@ class fakesock(object):
|
||||
self.truesock.close()
|
||||
|
||||
def sendall(self, data):
|
||||
self._sent_data.append(data)
|
||||
hostnames = [i.hostname for i in HTTPretty._entries.keys()]
|
||||
self.fd.seek(0)
|
||||
try:
|
||||
verb, headers_string = data.split('\n', 1)
|
||||
is_parsing_headers = True
|
||||
except ValueError:
|
||||
return self._true_sendall(data)
|
||||
is_parsing_headers = False
|
||||
|
||||
if self._host not in hostnames:
|
||||
return self._true_sendall(data)
|
||||
|
||||
if not is_parsing_headers:
|
||||
if len(self._sent_data) > 1:
|
||||
headers, body = self._sent_data[-2:]
|
||||
try:
|
||||
return HTTPretty.historify_request(headers, body)
|
||||
|
||||
except Exception, e:
|
||||
logging.error(traceback.format_exc(e))
|
||||
return self._true_sendall(data)
|
||||
|
||||
method, path, version = re.split('\s+', verb.strip(), 3)
|
||||
|
||||
info = URIInfo(hostname=self._host, port=self._port, path=path)
|
||||
request = HTTPretty.historify_request(data)
|
||||
|
||||
info = URIInfo(hostname=self._host, port=self._port, path=path,
|
||||
last_request=request)
|
||||
|
||||
entries = []
|
||||
for key, value in HTTPretty._entries.items():
|
||||
@@ -272,8 +313,14 @@ class Entry(object):
|
||||
string_list.append('Date: %s' % headers.pop('Date'))
|
||||
|
||||
if not self.forcing_headers:
|
||||
string_list.append('Content-Type: %s' % headers.pop('Content-Type', 'text/plain; charset=utf-8'))
|
||||
string_list.append('Content-Length: %s' % headers.pop('Content-Length', len(self.body)))
|
||||
content_type = headers.pop('Content-Type',
|
||||
'text/plain; charset=utf-8')
|
||||
content_length = headers.pop('Content-Length', len(self.body))
|
||||
string_list.append(
|
||||
'Content-Type: %s' % content_type)
|
||||
string_list.append(
|
||||
'Content-Length: %s' % content_length)
|
||||
|
||||
string_list.append('Server: %s' % headers.pop('Server')),
|
||||
|
||||
for k, v in headers.items():
|
||||
@@ -286,8 +333,18 @@ class Entry(object):
|
||||
fk.write(self.body)
|
||||
fk.seek(0)
|
||||
|
||||
|
||||
class URIInfo(object):
|
||||
def __init__(self, username='', password='', hostname='', port=80, path='/', query='', fragment='', entries=None):
|
||||
def __init__(self,
|
||||
username='',
|
||||
password='',
|
||||
hostname='',
|
||||
port=80,
|
||||
path='/',
|
||||
query='',
|
||||
fragment='',
|
||||
entries=None,
|
||||
last_request=None):
|
||||
self.username = username or ''
|
||||
self.password = password or ''
|
||||
self.hostname = hostname or ''
|
||||
@@ -297,6 +354,7 @@ class URIInfo(object):
|
||||
self.fragment = fragment or ''
|
||||
self.entries = entries
|
||||
self.current_entry = 0
|
||||
self.last_request = last_request
|
||||
|
||||
def get_next_entry(self):
|
||||
if self.current_entry >= len(self.entries):
|
||||
@@ -340,12 +398,20 @@ class URIInfo(object):
|
||||
class HTTPretty(object):
|
||||
u"""The URI registration class"""
|
||||
_entries = {}
|
||||
|
||||
latest_requests = []
|
||||
GET = 'GET'
|
||||
PUT = 'PUT'
|
||||
POST = 'POST'
|
||||
DELETE = 'DELETE'
|
||||
HEAD = 'HEAD'
|
||||
PATCH = 'PATCH'
|
||||
|
||||
@classmethod
|
||||
def historify_request(cls, headers, body=''):
|
||||
request = HTTPrettyRequest(headers, body)
|
||||
cls.last_request = request
|
||||
cls.latest_requests.append(request)
|
||||
return request
|
||||
|
||||
@classmethod
|
||||
def register_uri(cls, method, uri, body='HTTPretty :)', adding_headers=None, forcing_headers=None, status=200, responses=None, **headers):
|
||||
|
||||
6
requirements.txt
Normal file
6
requirements.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
nose
|
||||
sure
|
||||
bolacha
|
||||
httplib2
|
||||
tornado
|
||||
multiprocessing
|
||||
@@ -47,6 +47,8 @@ def test_httpretty_should_mock_a_simple_get_with_httplib2_read(context, now):
|
||||
|
||||
_, got = context.http.request('http://globo.com', 'GET')
|
||||
assert that(got).equals('The biggest portal in Brazil')
|
||||
assert that(HTTPretty.last_request.method).equals('GET')
|
||||
assert that(HTTPretty.last_request.path).equals('/')
|
||||
|
||||
@within(two=microseconds)
|
||||
@that_with_context(prepare, and_clear)
|
||||
@@ -166,3 +168,28 @@ def test_httpretty_should_support_a_list_of_successive_responses_httplib2(contex
|
||||
assert that(headers3['status']).equals('202')
|
||||
assert that(body3).equals('second and last response')
|
||||
|
||||
|
||||
@within(two=microseconds)
|
||||
@that_with_context(prepare, and_clear)
|
||||
def test_can_inspect_last_request(context, now):
|
||||
u"HTTPretty.last_request is a mimetools.Message request from last match"
|
||||
|
||||
HTTPretty.register_uri(HTTPretty.POST, "http://api.github.com/",
|
||||
body='{"repositories": ["HTTPretty", "lettuce"]}')
|
||||
|
||||
headers, body = context.http.request(
|
||||
'http://api.github.com', 'POST',
|
||||
body='{"username": "gabrielfalcao"}',
|
||||
headers={
|
||||
'content-type': 'text/json',
|
||||
},
|
||||
)
|
||||
|
||||
assert that(HTTPretty.last_request.method).equals('POST')
|
||||
assert that(HTTPretty.last_request.body).equals(
|
||||
'{"username": "gabrielfalcao"}',
|
||||
)
|
||||
assert that(HTTPretty.last_request.headers['content-type']).equals(
|
||||
'text/json',
|
||||
)
|
||||
assert that(body).equals('{"repositories": ["HTTPretty", "lettuce"]}')
|
||||
|
||||
@@ -28,6 +28,7 @@ import urllib2
|
||||
from sure import *
|
||||
from httpretty import HTTPretty
|
||||
|
||||
|
||||
@within(two=microseconds)
|
||||
def test_httpretty_should_mock_a_simple_get_with_urllib2_read():
|
||||
u"HTTPretty should mock a simple GET with urllib2.read()"
|
||||
@@ -41,6 +42,7 @@ def test_httpretty_should_mock_a_simple_get_with_urllib2_read():
|
||||
|
||||
assert that(got).equals('The biggest portal in Brazil')
|
||||
|
||||
|
||||
@within(two=microseconds)
|
||||
def test_httpretty_should_mock_headers_urllib2(now):
|
||||
u"HTTPretty should mock basic headers with urllib2"
|
||||
@@ -87,9 +89,10 @@ def test_httpretty_should_allow_adding_and_overwritting_urllib2(now):
|
||||
'content-length': '27',
|
||||
'status': '200 OK',
|
||||
'server': 'Apache',
|
||||
'date': now.strftime('%a, %d %b %Y %H:%M:%S GMT')
|
||||
'date': now.strftime('%a, %d %b %Y %H:%M:%S GMT'),
|
||||
})
|
||||
|
||||
|
||||
@within(two=microseconds)
|
||||
def test_httpretty_should_allow_forcing_headers_urllib2():
|
||||
u"HTTPretty should allow forcing headers with urllib2"
|
||||
@@ -131,7 +134,7 @@ def test_httpretty_should_allow_adding_and_overwritting_by_kwargs_u2(now):
|
||||
'content-length': '23456789',
|
||||
'status': '200 OK',
|
||||
'server': 'Apache',
|
||||
'date': now.strftime('%a, %d %b %Y %H:%M:%S GMT')
|
||||
'date': now.strftime('%a, %d %b %Y %H:%M:%S GMT'),
|
||||
})
|
||||
|
||||
|
||||
@@ -163,3 +166,31 @@ def test_httpretty_should_support_a_list_of_successive_responses_urllib2(now):
|
||||
request3.close()
|
||||
assert that(request3.code).equals(202)
|
||||
assert that(body3).equals('second and last response')
|
||||
|
||||
|
||||
@within(two=microseconds)
|
||||
def test_can_inspect_last_request(now):
|
||||
u"HTTPretty.last_request is a mimetools.Message request from last match"
|
||||
|
||||
HTTPretty.register_uri(HTTPretty.POST, "http://api.github.com/",
|
||||
body='{"repositories": ["HTTPretty", "lettuce"]}')
|
||||
|
||||
request = urllib2.Request(
|
||||
'http://api.github.com',
|
||||
'{"username": "gabrielfalcao"}',
|
||||
{
|
||||
'content-type': 'text/json',
|
||||
},
|
||||
)
|
||||
fd = urllib2.urlopen(request)
|
||||
got = fd.read()
|
||||
fd.close()
|
||||
|
||||
assert that(HTTPretty.last_request.method).equals('POST')
|
||||
assert that(HTTPretty.last_request.body).equals(
|
||||
'{"username": "gabrielfalcao"}',
|
||||
)
|
||||
assert that(HTTPretty.last_request.headers['content-type']).equals(
|
||||
'text/json',
|
||||
)
|
||||
assert that(got).equals('{"repositories": ["HTTPretty", "lettuce"]}')
|
||||
|
||||
Reference in New Issue
Block a user