diff options
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/.markment.yml | 23 | ||||
| -rw-r--r-- | docs/NEWS.md | 80 | ||||
| -rw-r--r-- | docs/about.md | 18 | ||||
| -rw-r--r-- | docs/contributing.md | 62 | ||||
| -rw-r--r-- | docs/docs.md | 299 | ||||
| -rw-r--r-- | docs/intro.md | 82 |
6 files changed, 564 insertions, 0 deletions
diff --git a/docs/.markment.yml b/docs/.markment.yml new file mode 100644 index 0000000..f669aba --- /dev/null +++ b/docs/.markment.yml @@ -0,0 +1,23 @@ +project: + name: "HTTPretty" + version: 0.7.0 + description: HTTP request mock tool for python + tagline: Intercept real HTTP calls in python, test your software better. + twitter: gabrielfalcao + twitter_message: Python%20library%20for%20intercepting%20and%20mocking%20HTTP%20calls + google_analytics_code: UA-1277640-14 + github_maintainer: gabrielfalcao + github_maintainer_name: Gabriel Falcão + github_maintainer_url: http://github.com/gabrielfalcao + github_maintainer_gravatar: https://secure.gravatar.com/avatar/3fa0df5c54f5ac0f8652d992d7d24039?s=64 + + github_url: http://github.com/gabrielfalcao/HTTPretty + github_fork_url: https://github.com/gabrielfalcao/HTTPretty/archive/master.tar.gz + tarball_download_url: https://github.com/gabrielfalcao/HTTPretty/archive/master.tar.gz + zipball_download_url: https://github.com/gabrielfalcao/HTTPretty/archive/master.zip + documentation_url: http://falcao.it/HTTPretty + support_url: http://github.com/gabrielfalcao/HTTPretty/issues + logo_url: https://s3-us-west-2.amazonaws.com/s.cdpn.io/18885/httpretty-logo_1.svg + full_index: false +documentation: + index: intro.md diff --git a/docs/NEWS.md b/docs/NEWS.md new file mode 100644 index 0000000..d8fb0e1 --- /dev/null +++ b/docs/NEWS.md @@ -0,0 +1,80 @@ +# Release Notes + +## 0.7.0 (current) + +Improvements: + +* Refactored `core.py` and increased its unit test coverage to 80%. HTTPretty is slightly more robust now. + +Bug fixes: + +* POST requests being called twice [#100](https://github.com/gabrielfalcao/HTTPretty/pull/100) + +## 0.6.5 + +Applied pull requests: + +* continue on EAGAIN socket errors: [#102](https://github.com/gabrielfalcao/HTTPretty/pull/102) by [kouk](http://github.com/kouk). +* Fix `fake_gethostbyname` for requests 2.0: [#101](https://github.com/gabrielfalcao/HTTPretty/pull/101) by [mgood](http://github.com/mgood) +* Add a way to match the querystrings: [#98](https://github.com/gabrielfalcao/HTTPretty/pull/98) by [ametaireau](http://github.com/ametaireau) +* Use common string case for URIInfo hostname comparison: [#95](https://github.com/gabrielfalcao/HTTPretty/pull/95) by [mikewaters](http://github.com/mikewaters) +* Expose httpretty.reset() to public API: [#91](https://github.com/gabrielfalcao/HTTPretty/pull/91) by [imankulov](http://github.com/imankulov) +* Don't duplicate http ports number: [#89](https://github.com/gabrielfalcao/HTTPretty/pull/89) by [mardiros](http://github.com/mardiros) +* Adding parsed_body parameter to simplify checks: [#88](https://github.com/gabrielfalcao/HTTPretty/pull/88) by [toumorokoshi](http://github.com/toumorokoshi) +* Use the real socket if it's not HTTP: [#87](https://github.com/gabrielfalcao/HTTPretty/pull/87) by [mardiros](http://github.com/mardiros) + +## 0.6.2 + +* Fixing bug of lack of trailing slashes [#73](https://github.com/gabrielfalcao/HTTPretty/issues/73) +* Applied pull requests [#71](https://github.com/gabrielfalcao/HTTPretty/pull/71) and [#72](https://github.com/gabrielfalcao/HTTPretty/pull/72) by @andresriancho +* Keyword arg coercion fix by @dupuy +* @papaeye fixed content-length calculation. + +## 0.6.1 + +* New API, no more camel case and everything is available through a simple import: + +```python +import httpretty + +@httpretty.activate +def test_function(): + # httpretty.register_uri(...) + # make request... + pass +``` + +* Re-organized module into submodules + +## 0.5.14 + +* Delegate calls to other methods on socket + +* [Normalized header](https://github.com/gabrielfalcao/HTTPretty/pull/49) strings + +* Callbacks are [more intelligent now](https://github.com/gabrielfalcao/HTTPretty/pull/47) + +* Normalize urls matching for url quoting + +## 0.5.12 + +* HTTPretty doesn't hang when using other application protocols under + a @httprettified decorated test. + +## 0.5.11 + +* Ability to know whether HTTPretty is or not enabled through `httpretty.is_enabled()` + +## 0.5.10 + +* Support to multiple methods per registered URL. Thanks @hughsaunders + +## 0.5.9 + +* Fixed python 3 support. Thanks @spulec + +## 0.5.8 + +* Support to [register regular expressions to match urls](#matching-regular-expressions) +* [Body callback](#dynamic-responses-through-callbacks) suppport +* Python 3 support diff --git a/docs/about.md b/docs/about.md new file mode 100644 index 0000000..44b87a7 --- /dev/null +++ b/docs/about.md @@ -0,0 +1,18 @@ +# Acknowledgements + +## caveats + +### `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 + +## supported libraries + +Because HTTPretty works in the socket level it should work with any HTTP client libraries, although it is [battle tested](https://github.com/gabrielfalcao/HTTPretty/tree/master/tests/functional) against: + +* [requests](http://docs.python-requests.org/en/latest/) +* [httplib2](http://code.google.com/p/httplib2/) +* [urllib2](http://docs.python.org/2/library/urllib2.html) diff --git a/docs/contributing.md b/docs/contributing.md new file mode 100644 index 0000000..e4ed3ae --- /dev/null +++ b/docs/contributing.md @@ -0,0 +1,62 @@ +# Hacking on HTTPretty + +## creating a virtual env + +you will need [virtualenvwrapper](http://www.doughellmann.com/projects/virtualenvwrapper/) + + +```console +mkvirtualenv --distribute --no-site-packages HTTPretty +``` + +## installing the dependencies + +```console +pip install -r requirements.pip +``` + +## next steps + +1. run the tests with make: +```bash +make unit functional +``` +2. hack at will +3. commit, push etc +4. send a pull request + +# License + + <HTTPretty - HTTP client mock for Python> + Copyright (C) <2011-2013> Gabriel Falcão <gabriel@nacaolivre.org> + + 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. + + +# Main contributors + +There folks made remarkable contributions to HTTPretty: + +* Steve Pulec ~> @spulec +* Hugh Saunders ~> @hughsaunders +* Matt Luongo ~> @mhluongo +* James Rowe ~> @JNRowe diff --git a/docs/docs.md b/docs/docs.md new file mode 100644 index 0000000..c87f401 --- /dev/null +++ b/docs/docs.md @@ -0,0 +1,299 @@ +# Reference + +## testing query strings + +```python +import requests +from sure import expect +import httpretty + +def test_one(): + httpretty.enable() # enable HTTPretty so that it will monkey patch the socket module + httpretty.register_uri(httpretty.GET, "http://yipit.com/login", + body="Find the best daily deals") + + requests.get('http://yipit.com/login?email=user@github.com&password=foobar123') + expect(httpretty.last_request()).to.have.property("querystring").being.equal({ + "email": "user@github.com", + "password": "foobar123", + }) + + httpretty.disable() # disable afterwards, so that you will have no problems in code that uses that socket module +``` + + +## Using the decorator + +**YES** we've got a decorator + +```python +import requests +import httpretty + +@httpretty.activate +def test_one(): + httpretty.register_uri(httpretty.GET, "http://yipit.com/", + body="Find the best daily deals") + + response = requests.get('http://yipit.com') + assert response.text == "Find the best daily deals" +``` + +the `@httpretty.activate` is a short-hand decorator that wraps the +decorated function with httpretty.enable() and then calls +httpretty.disable() right after. + +## Providing status code + +```python +import requests +from sure import expect +import httpretty + +@httpretty.activate +def test_github_access(): + httpretty.register_uri(httpretty.GET, "http://github.com/", + body="here is the mocked body", + status=201) + + response = requests.get('http://github.com') + expect(response.status_code).to.equal(201) +``` + +## Providing custom heades + +**and all you need is to add keyword args in which the keys are always lower-cased and with underscores `_` instead of dashes `-`** + +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.activate +def test_some_api(): + httpretty.register_uri(httpretty.GET, "http://foo-api.com/gabrielfalcao", + body='{"success": false}', + status=500, + content_type='text/json') + + response = requests.get('http://foo-api.com/gabrielfalcao') + + expect(response.json()).to.equal({'success': False}) + expect(response.status_code).to.equal(500) +``` + + +### Adding extra headers and forcing headers + +You can pass the `adding_headers` argument as a dictionary and your +headers will be +[united](http://en.wikipedia.org/wiki/Union_(set_theory)) to the +existing headers. + +```python +@httpretty.activate +def test_some_api(): + httpretty.register_uri(httpretty.GET, "http://foo-api.com/gabrielfalcao", + body='{"success": false}', + status=500, + content_type='text/json', + adding_headers={ + 'X-foo': 'bar' + }) + + response = requests.get('http://foo-api.com/gabrielfalcao') + + expect(response.json()).to.equal({'success': False}) + expect(response.status_code).to.equal(500) +``` + +Although there are some situation where some headers line +`content-length` will be calculated by HTTPretty based on the +specified fake response body. + +So you might want to *"force"* those headers: + +```python +@httpretty.activate +def test_some_api(): + httpretty.register_uri(httpretty.GET, "http://foo-api.com/gabrielfalcao", + body='{"success": false}', + status=500, + content_type='text/json', + forcing_headers={ + 'content-length': '100' + }) + + response = requests.get('http://foo-api.com/gabrielfalcao') + + expect(response.json()).to.equal({'success': False}) + expect(response.status_code).to.equal(500) +``` + +You should, though, be careful with it. The HTTP client is likely to +rely on the content length to know how many bytes of response payload +should be loaded. Forcing a `content-length` that is bigger than the +action response body might cause the HTTP client to hang because it is +waiting for data. Read more in the "caveats" session on the bottom. + +## rotating responses + +Same URL, same request method, the first request return the first +httpretty.Response, all the subsequent ones return the last (status 202). + +Notice that the `responses` argument is a list and you can pass as +many responses as you want. + +```python +import requests +from sure import expect + + +@httpretty.activate +def test_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), + ]) + + response1 = requests.get('http://github.com/gabrielfalcao/httpretty') + expect(response1.status_code).to.equal(201) + expect(response1.text).to.equal('first response') + + response2 = requests.get('http://github.com/gabrielfalcao/httpretty') + expect(response2.status_code).to.equal(202) + expect(response2.text).to.equal('second and last response') + + response3 = requests.get('http://github.com/gabrielfalcao/httpretty') + + expect(response3.status_code).to.equal(202) + expect(response3.text).to.equal('second and last response') +``` +## streaming responses + +Mock a streaming response by registering a generator response body. + +```python +import requests +from sure import expect +import httpretty + +# mock a streaming response body with a generator +def mock_streaming_tweets(tweets): + from time import sleep + for t in tweets: + sleep(.5) + yield t + +@httpretty.activate +def test_twitter_api_integration(now): + twitter_response_lines = [ + '{"text":"If @BarackObama requests to follow me one more time I\'m calling the police."}\r\n', + '\r\n', + '{"text":"Thanks for all your #FollowMe1D requests Directioners! We\u2019ll be following 10 people throughout the day starting NOW. G ..."}\r\n' + ] + + TWITTER_STREAMING_URL = "https://stream.twitter.com/1/statuses/filter.json" + + # set the body to a generator and set `streaming=True` to mock a streaming response body + httpretty.register_uri(httpretty.POST, TWITTER_STREAMING_URL, + body=mock_streaming_tweets(twitter_response_lines), + streaming=True) + + # taken from the requests docs + # http://docs.python-requests.org/en/latest/user/advanced/#streaming-requests + response = requests.post(TWITTER_STREAMING_URL, data={'track':'requests'}, + auth=('username','password'), prefetch=False) + + #test iterating by line + line_iter = response.iter_lines() + for i in xrange(len(twitter_response_lines)): + expect(line_iter.next().strip()).to.equal(twitter_response_lines[i].strip()) +``` + +## dynamic responses through callbacks + +Set a callback to allow for dynamic responses based on the request. + +```python +import requests +from sure import expect +import httpretty + +@httpretty.activate +def test_response_callbacks(): + + def request_callback(method, uri, headers): + return (200, headers, "The {} response from {}".format(method, uri)) + + httpretty.register_uri( + httpretty.GET, "https://api.yahoo.com/test", + body=request_callback) + + response = requests.get('https://api.yahoo.com/test') + + expect(response.text).to.equal('The GET response from https://api.yahoo.com/test') +``` + +## matching regular expressions + +You can register a +[compiled regex](http://docs.python.org/2/library/re.html#re.compile) +and it will be matched against the requested urls. + +```python +@httpretty.activate +def test_httpretty_should_allow_registering_regexes(): + u"HTTPretty should allow registering regexes" + + httpretty.register_uri( + httpretty.GET, + re.compile("api.yipit.com/v2/deal;brand=(\w+)"), + body="Found brand", + ) + + response = requests.get('https://api.yipit.com/v2/deal;brand=GAP') + expect(response.text).to.equal('Found brand') + expect(httpretty.last_request().method).to.equal('GET') + expect(httpretty.last_request().path).to.equal('/v1/deal;brand=GAP') +``` + +By default, the regexp you register will match the requests without looking at +the querystring. If you want the querystring to be considered, you can set +`match_querystring=True` when calling `register_uri`. + +## 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 +import httpretty + + +@httpretty.activate +def test_yipit_api_integration(): + httpretty.register_uri(httpretty.POST, "http://api.yipit.com/foo/", + body='{"repositories": ["HTTPretty", "lettuce"]}') + + 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') +``` + +## checking if is enabled + +```python + +httpretty.enable() +httpretty.is_enabled().should.be.true + +httpretty.disable() +httpretty.is_enabled().should.be.false + +``` diff --git a/docs/intro.md b/docs/intro.md new file mode 100644 index 0000000..7187a65 --- /dev/null +++ b/docs/intro.md @@ -0,0 +1,82 @@ +# What is HTTPretty ? + +Once upon a time a python developer wanted to use a RESTful api, +everything was fine but until the day he needed to test the code that +hits the RESTful API: what if the API server is down? What if its +content has changed ? + +Don't worry, HTTPretty is here for you: + +```python +import requests +from sure import expect +import httpretty + + +@httpretty.activate +def test_yipit_api_returning_deals(): + httpretty.register_uri(httpretty.GET, "http://api.yipit.com/v1/deals/", + body='[{"title": "Test Deal"}]', + content_type="application/json") + + response = requests.get('http://api.yipit.com/v1/deals/') + + expect(response.json()).to.equal([{"title": "Test Deal"}]) +``` + +## A more technical description + +HTTPretty is a HTTP client mock library for Python 100% inspired on ruby's [FakeWeb](http://fakeweb.rubyforge.org/). +If you come from ruby this would probably sound familiar :smiley: + +## Installing + +Installing httpretty is as easy as: + +```bash +pip install HTTPretty +``` + +# Demo + +## expecting a simple response body + +```python +import requests +import httpretty + +def test_one(): + httpretty.enable() # enable HTTPretty so that it will monkey patch the socket module + httpretty.register_uri(httpretty.GET, "http://yipit.com/", + body="Find the best daily deals") + + response = requests.get('http://yipit.com') + + assert response.text == "Find the best daily deals" + + httpretty.disable() # disable afterwards, so that you will have no problems in code that uses that socket module + httpretty.reset() # reset HTTPretty state (clean up registered urls and request history) +``` + +# Motivation + +When building systems that access external resources such as RESTful +webservices, XMLRPC or even simple HTTP requests, we stumble in the +problem: + + "I'm gonna need to mock all those requests" + +It brings a lot of hassle, you will need to use a generic mocking +tool, mess with scope and so on. + +## The idea behind HTTPretty (how it works) + +HTTPretty [monkey patches](http://en.wikipedia.org/wiki/Monkey_patch) +Python's [socket](http://docs.python.org/library/socket.html) core +module, reimplementing the HTTP protocol, by mocking requests and +responses. + +As for it works in this way, you don't need to worry what http library +you're gonna use. + +HTTPretty will mock the response for you :) *(and also give you the latest requests so that you can check them)* |
