version 0.2, recording the requests

This commit is contained in:
Gabriel Falcao
2011-06-26 13:23:45 -04:00
parent ed5d8c57c7
commit e74bd02011
5 changed files with 197 additions and 39 deletions

View File

@@ -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/)

View File

@@ -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
View File

@@ -0,0 +1,6 @@
nose
sure
bolacha
httplib2
tornado
multiprocessing

View File

@@ -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"]}')

View File

@@ -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"]}')