From 6b940c149f2e3fa4496b52a528579bdfa7eb638b Mon Sep 17 00:00:00 2001 From: Gabriel Falcao Date: Wed, 7 Nov 2012 23:45:54 -0500 Subject: [PATCH] updating readme and some tests --- README.md | 84 +++++++----- httpretty/__init__.py | 22 ++- tests/functional/test_httplib2.py | 8 +- tests/functional/test_requests.py | 218 ++++++++++++++++++++++++++++++ tests/functional/test_urllib2.py | 8 +- 5 files changed, 294 insertions(+), 46 deletions(-) create mode 100644 tests/functional/test_requests.py diff --git a/README.md b/README.md index a46bc6b..2fc00fd 100644 --- a/README.md +++ b/README.md @@ -39,18 +39,19 @@ HTTPretty will mock the response for you :) *(and also give you the latest reque ## expecting a simple response body ```python +import urllib2 from httpretty import HTTPretty def test_one(): HTTPretty.enable() # enable HTTPretty so that it will monkey patch the socket module - HTTPretty.register_uri(HTTPretty.GET, "http://globo.com/", - body="The biggest portal in Brazil") + HTTPretty.register_uri(HTTPretty.GET, "http://yipit.com/", + body="Find the best daily deals") - fd = urllib2.urlopen('http://globo.com') + fd = urllib2.urlopen('http://yipit.com') got = fd.read() fd.close() - assert got == "The biggest portal in Brazil" + assert got == "Find the best daily deals" HTTPretty.disable() # disable afterwards, so that you will have no problems in coda that uses that socket module ``` @@ -60,18 +61,16 @@ def test_one(): **YES** we've got a decorator ```python +import requests from httpretty import HTTPretty, httprettified @httprettified def test_one(): - HTTPretty.register_uri(HTTPretty.GET, "http://globo.com/", - body="The biggest portal in Brazil") + HTTPretty.register_uri(HTTPretty.GET, "http://yipit.com/", + body="Find the best daily deals") - fd = urllib2.urlopen('http://globo.com') - got = fd.read() - fd.close() - - assert got == "The biggest portal in Brazil" + response = requests.get('http://yipit.com') + assert response.text == "Find the best daily deals" ``` the `@httprettified` is a short-hand decorator that wraps the @@ -81,6 +80,8 @@ HTTPretty.disable() right after. ## mocking the status code ```python +import json +import urllib2 from httpretty import HTTPretty, httprettified @httprettified @@ -105,16 +106,18 @@ For example, let's say you want to mock that server returns `content-type`. To do so, use the argument `content_type`, **all the keyword args are taken by HTTPretty and transformed in the RFC2616 equivalent name**. ```python -HTTPretty.register_uri(HTTPretty.GET, "http://github.com/", +import urllib2 + +HTTPretty.register_uri(HTTPretty.GET, "http://github.com/gabrielfalcao", body='{"success": false}', status=500, content_type='text/json') -fd = urllib2.urlopen('http://github.com') +fd = urllib2.urlopen('http://github.com/gabrielfalcao') got = fd.read() fd.close() -assert simplejson.loads(got)['success'] is False +assert json.loads(got)['success'] is False assert fd.code == 500 ``` @@ -127,6 +130,9 @@ HTTPretty.Response, all the subsequent ones return the last (status 202) ```python +import urllib2 +from sure import expect + HTTPretty.register_uri(HTTPretty.GET, "http://github.com/gabrielfalcao/httpretty", responses=[ HTTPretty.Response(body="first response", status=201), @@ -137,42 +143,56 @@ request1 = urllib2.urlopen('http://github.com/gabrielfalcao/httpretty') body1 = request1.read() request1.close() -assert that(request1.code).equals(201) -assert that(body1).equals('first response') +expect(request1.code).to.equal(201) +expect(body1).to.equal('first 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') + +expect(request2.code).to.equal(202) +expect(body2).to.equal('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') +expect(request3.code).to.equal(202) +expect(body3).to.equal('second and last response') ``` ## expect for a response, and check the request got by the "server" to make sure it was fine. ```python +import requests +from sure import expect from httpretty import HTTPretty -from httplib2 import Http -HTTPretty.register_uri(HTTPretty.PATCH, "http://api.github.com/", + +HTTPretty.register_uri(HTTPretty.POST, "http://api.yipit.com/foo", 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' +response = requests.post('http://api.yipit.com/foo', + '{"username": "gabrielfalcao"}', + headers={ + 'content-type': 'text/json', + }) + +expect(response.text).to.equal('{"repositories": ["HTTPretty", "lettuce"]}') +expect(HTTPretty.last_request.method).to.equal("POST") +expect(HTTPretty.last_request.headers['content-type']).to.equal('text/json') ``` +# Acknowledgements + +## caveats with the [requests](http://docs.python-requests.org/en/latest/) library + +### `forcing_headers` + `Content-Length` + +if you use the `forcing_headers` options make sure to add the header +`Content-Length` otherwise the +[requests](http://docs.python-requests.org/en/latest/) will try to +load the response endlessly + # Dependencies you will need **ONLY** if you decide to contribute to HTTPretty which @@ -190,7 +210,7 @@ means you're gonna need run our test suite ### I know you want it :) ```bash -pip install -r requirements.txt +pip install -r requirements.pip ``` # Contributing diff --git a/httpretty/__init__.py b/httpretty/__init__.py index 61dd537..6c028b4 100644 --- a/httpretty/__init__.py +++ b/httpretty/__init__.py @@ -68,10 +68,17 @@ class HTTPrettyError(Exception): pass +def utf8(s): + if isinstance(s, unicode): + s = s.encode('utf-8') + + return str(s) + + class HTTPrettyRequest(BaseHTTPRequestHandler): def __init__(self, headers, body=''): - self.body = body - self.raw_headers = headers + self.body = utf8(body) + self.raw_headers = utf8(headers) self.rfile = StringIO(headers + body) self.raw_requestline = self.rfile.readline() @@ -215,7 +222,7 @@ class fakesock(object): if not is_parsing_headers: if len(self._sent_data) > 1: - headers, body = self._sent_data[-2:] + headers, body = map(utf8, self._sent_data[-2:]) try: return HTTPretty.historify_request(headers, body) @@ -225,7 +232,7 @@ class fakesock(object): method, path, version = re.split('\s+', verb.strip(), 3) - headers, body = data.split('\r\n\r\n') + headers, body = map(utf8, data.split('\r\n\r\n')) request = HTTPretty.historify_request(headers, body) @@ -250,6 +257,9 @@ class fakesock(object): def debug(*a, **kw): import debug + def settimeout(self, new_timeout): + self.timeout = new_timeout + sendto = send = recvfrom_into = recv_into = recvfrom = recv = debug @@ -426,7 +436,7 @@ class Entry(object): for k, v in headers.items(): string_list.append( - '%s: %s' % (k, unicode(v)), + '%s: %s' % (k, utf8(v)), ) fk.write("\n".join(string_list)) @@ -570,7 +580,7 @@ class HTTPretty(object): def Response(cls, body, adding_headers=None, forcing_headers=None, status=200, **headers): - headers['body'] = body + headers['body'] = utf8(body) headers['adding_headers'] = adding_headers headers['forcing_headers'] = forcing_headers headers['status'] = int(status) diff --git a/tests/functional/test_httplib2.py b/tests/functional/test_httplib2.py index 0e18093..ee802b8 100644 --- a/tests/functional/test_httplib2.py +++ b/tests/functional/test_httplib2.py @@ -35,11 +35,11 @@ from httpretty import HTTPretty, httprettified def test_httpretty_should_mock_a_simple_get_with_httplib2_read(now): u"HTTPretty should mock a simple GET with httplib2.context.http" - HTTPretty.register_uri(HTTPretty.GET, "http://globo.com/", - body="The biggest portal in Brazil") + HTTPretty.register_uri(HTTPretty.GET, "http://yipit.com/", + body="Find the best daily deals") - _, got = httplib2.Http().request('http://globo.com', 'GET') - assert that(got).equals('The biggest portal in Brazil') + _, got = httplib2.Http().request('http://yipit.com', 'GET') + assert that(got).equals('Find the best daily deals') assert that(HTTPretty.last_request.method).equals('GET') assert that(HTTPretty.last_request.path).equals('/') diff --git a/tests/functional/test_requests.py b/tests/functional/test_requests.py new file mode 100644 index 0000000..f256f5d --- /dev/null +++ b/tests/functional/test_requests.py @@ -0,0 +1,218 @@ +# #!/usr/bin/env python +# -*- coding: utf-8 -*- + +# +# Copyright (C) <2011> Gabriel Falcão +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, +# copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following +# conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# 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. + +import requests +from sure import that, within, microseconds +from httpretty import HTTPretty, httprettified + + +@httprettified +@within(two=microseconds) +def test_httpretty_should_mock_a_simple_get_with_requests_read(now): + u"HTTPretty should mock a simple GET with requests.get" + + HTTPretty.register_uri(HTTPretty.GET, "http://yipit.com/", + body="Find the best daily deals") + + response = requests.get('http://yipit.com') + assert that(response.text).equals('Find the best daily deals') + assert that(HTTPretty.last_request.method).equals('GET') + assert that(HTTPretty.last_request.path).equals('/') + + +@httprettified +@within(two=microseconds) +def test_httpretty_should_mock_headers_requests(now): + u"HTTPretty should mock basic headers with requests" + + HTTPretty.register_uri(HTTPretty.GET, "http://github.com/", + body="this is supposed to be the response", + status=201) + + response = requests.get('http://github.com') + assert that(response.status_code).equals(201) + + assert that(dict(response.headers)).equals({ + 'content-type': 'text/plain; charset=utf-8', + 'connection': 'close', + 'content-length': '35', + 'status': '201', + 'server': 'Python/HTTPretty', + 'date': now.strftime('%a, %d %b %Y %H:%M:%S GMT'), + }) + + +@httprettified +@within(two=microseconds) +def test_httpretty_should_allow_adding_and_overwritting_requests(now): + u"HTTPretty should allow adding and overwritting headers with requests" + + HTTPretty.register_uri(HTTPretty.GET, "http://github.com/foo", + body="this is supposed to be the response", + adding_headers={ + 'Server': 'Apache', + 'Content-Length': '27', + 'Content-Type': 'application/json', + }) + + response = requests.get('http://github.com/foo') + + assert that(dict(response.headers)).equals({ + 'content-type': 'application/json', + 'connection': 'close', + 'content-length': '27', + 'status': '200', + 'server': 'Apache', + 'date': now.strftime('%a, %d %b %Y %H:%M:%S GMT'), + }) + + +@httprettified +@within(two=microseconds) +def test_httpretty_should_allow_forcing_headers_requests(now): + u"HTTPretty should allow forcing headers with requests" + + HTTPretty.register_uri(HTTPretty.GET, "http://github.com/foo", + body="", + forcing_headers={ + 'Content-Type': 'application/xml', + 'Content-Length': '19', + }) + + response = requests.get('http://github.com/foo') + + assert that(dict(response.headers)).equals({ + 'content-type': 'application/xml', + 'content-length': '19', + }) + + +@httprettified +@within(two=microseconds) +def test_httpretty_should_allow_adding_and_overwritting_by_kwargs_u2(now): + u"HTTPretty should allow adding and overwritting headers by keyword args " \ + "with requests" + + HTTPretty.register_uri(HTTPretty.GET, "http://github.com/foo", + body="this is supposed to be the response", + server='Apache', + content_length='27', + content_type='application/json') + + response = requests.get('http://github.com/foo') + + assert that(dict(response.headers)).equals({ + 'content-type': 'application/json', + 'connection': 'close', + 'content-length': '27', + 'status': '200', + 'server': 'Apache', + 'date': now.strftime('%a, %d %b %Y %H:%M:%S GMT'), + }) + + +@httprettified +@within(two=microseconds) +def test_rotating_responses_with_requests(now): + u"HTTPretty should support rotating responses with requests" + + HTTPretty.register_uri( + HTTPretty.GET, "https://api.yahoo.com/test", + responses=[ + HTTPretty.Response(body="first response", status=201), + HTTPretty.Response(body='second and last response', status=202), + ]) + + response1 = requests.get( + 'https://api.yahoo.com/test') + + assert that(response1.status_code).equals(201) + assert that(response1.text).equals('first response') + + response2 = requests.get( + 'https://api.yahoo.com/test') + + assert that(response2.status_code).equals(202) + assert that(response2.text).equals('second and last response') + + response3 = requests.get( + 'https://api.yahoo.com/test') + + assert that(response3.status_code).equals(202) + assert that(response3.text).equals('second and last response') + + +@httprettified +@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"]}') + + response = requests.post( + 'http://api.github.com', + '{"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(response.json).equals({"repositories": ["HTTPretty", "lettuce"]}) + + +@httprettified +@within(two=microseconds) +def test_can_inspect_last_request_with_ssl(now): + u"HTTPretty.last_request is recorded even when mocking 'https' (SSL)" + + HTTPretty.register_uri(HTTPretty.POST, "https://secure.github.com/", + body='{"repositories": ["HTTPretty", "lettuce"]}') + + response = requests.post( + 'https://secure.github.com', + '{"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(response.json).equals({"repositories": ["HTTPretty", "lettuce"]}) diff --git a/tests/functional/test_urllib2.py b/tests/functional/test_urllib2.py index b1d049b..00f27cb 100644 --- a/tests/functional/test_urllib2.py +++ b/tests/functional/test_urllib2.py @@ -34,14 +34,14 @@ from httpretty import HTTPretty, httprettified def test_httpretty_should_mock_a_simple_get_with_urllib2_read(): u"HTTPretty should mock a simple GET with urllib2.read()" - HTTPretty.register_uri(HTTPretty.GET, "http://globo.com/", - body="The biggest portal in Brazil") + HTTPretty.register_uri(HTTPretty.GET, "http://yipit.com/", + body="Find the best daily deals") - fd = urllib2.urlopen('http://globo.com') + fd = urllib2.urlopen('http://yipit.com') got = fd.read() fd.close() - assert that(got).equals('The biggest portal in Brazil') + assert that(got).equals('Find the best daily deals') @httprettified