1
0
mirror of https://github.com/kevin1024/vcrpy.git synced 2025-12-10 09:35:34 +00:00

Substantial refactoring

This refactoring includes some PEP-8 compliance changes, as well as some more
complete testing to ensure that we're in fact serving everything out of
cassettes when we thing we are.

Incidentally, it also includes fixes for #3 and #4
This commit is contained in:
Dan Lecocq
2013-08-02 16:15:26 -07:00
committed by Kevin McCarthy
parent 3742e2fdc0
commit b488ca67fe
16 changed files with 652 additions and 401 deletions

30
tests/common.py Normal file
View File

@@ -0,0 +1,30 @@
# coding=utf-8
import os
import json
import shutil
import unittest
class TestVCR(unittest.TestCase):
fixtures = os.path.join('does', 'not', 'exist')
def tearDown(self):
# Remove th urllib2 fixtures if they exist
if os.path.exists(self.fixtures):
shutil.rmtree(self.fixtures)
def fixture(self, *names):
'''Return a path to the provided fixture'''
return os.path.join(self.fixtures, *names)
def assertBodiesEqual(self, one, two):
"""
httpbin.org returns a different `origin` header
each time, so strip this out since it makes testing
difficult.
"""
one, two = json.loads(one), json.loads(two)
del one['origin']
del two['origin']
self.assertEqual(one, two)

View File

@@ -0,0 +1,37 @@
- request:
body: null
headers: !!python/object:requests.structures.CaseInsensitiveDict
_store:
accept: !!python/tuple [Accept, '*/*']
accept-encoding: !!python/tuple [Accept-Encoding, 'gzip, deflate, compress']
user-agent: !!python/tuple [User-Agent, vcrpy-test]
host: seomoz.org
method: GET
port: 80
url: /
response:
body: {string: "<html>\r\n<head><title>301 Moved Permanently</title></head>\r\n<body
bgcolor=\"white\">\r\n<center><h1>301 Moved Permanently</h1></center>\r\n<hr><center>nginx</center>\r\n</body>\r\n</html>\r\n"}
headers: {accept-ranges: bytes, age: '0', connection: keep-alive, content-length: '178',
content-type: text/html, date: 'Mon, 05 Aug 2013 22:05:23 GMT', location: 'http://moz.com/',
server: nginx, server-name: dalmozwww03.dal.moz.com, via: 1.1 varnish, x-varnish: '836826051'}
status: {code: 301, message: Moved Permanently}
- request:
body: null
headers: !!python/object:requests.structures.CaseInsensitiveDict
_store:
accept: !!python/tuple [Accept, '*/*']
accept-encoding: !!python/tuple [Accept-Encoding, 'gzip, deflate, compress']
user-agent: !!python/tuple [User-Agent, vcrpy-test]
host: moz.com
method: GET
port: 80
url: /
response:
body: {string: ''}
headers: {accept-ranges: bytes, age: '2763', cache-control: 'no-cache, must-revalidate,
s-maxage=3600', connection: keep-alive, content-encoding: gzip, content-length: '0',
content-type: text/html, date: 'Mon, 05 Aug 2013 22:05:23 GMT', expires: 'Fri,
15 Oct 2004 12:00:00 GMT', server: nginx, server-name: dalmozwww02.dal.moz.com,
vary: Accept-Encoding, via: 1.1 varnish, x-varnish: 836826061 836743142}
status: {code: 200, message: OK}

View File

@@ -1,107 +1,34 @@
'''Basic tests about cassettes'''
# coding=utf-8
import os
import unittest
# Internal imports
import vcr
from vcr.cassette import Cassette
from .common import TestVCR
# External imports
import os
import urllib2
from urllib import urlencode
from utils import assert_httpbin_responses_equal
TEST_CASSETTE_FILE = 'cassettes/test_req.yaml'
class TestHttpRequest(unittest.TestCase):
def tearDown(self):
try:
os.remove(TEST_CASSETTE_FILE)
except OSError:
pass
def test_response_code(self):
code = urllib2.urlopen('http://httpbin.org/').getcode()
with vcr.use_cassette(TEST_CASSETTE_FILE):
self.assertEqual(code, urllib2.urlopen('http://httpbin.org/').getcode())
self.assertEqual(code, urllib2.urlopen('http://httpbin.org/').getcode())
def test_response_body(self):
body = urllib2.urlopen('http://httpbin.org/').read()
with vcr.use_cassette(TEST_CASSETTE_FILE):
self.assertEqual(body, urllib2.urlopen('http://httpbin.org/').read())
self.assertEqual(body, urllib2.urlopen('http://httpbin.org/').read())
def test_response_headers(self):
with vcr.use_cassette(TEST_CASSETTE_FILE):
headers = urllib2.urlopen('http://httpbin.org/').info().items()
self.assertEqual(headers, urllib2.urlopen('http://httpbin.org/').info().items())
def test_multiple_requests(self):
body1 = urllib2.urlopen('http://httpbin.org/').read()
body2 = urllib2.urlopen('http://httpbin.org/get').read()
with vcr.use_cassette(TEST_CASSETTE_FILE):
self.assertEqual(body1, urllib2.urlopen('http://httpbin.org/').read())
new_body2 = urllib2.urlopen('http://httpbin.org/get').read()
assert_httpbin_responses_equal(body2, new_body2)
self.assertEqual(body1, urllib2.urlopen('http://httpbin.org/').read())
new_body2 = urllib2.urlopen('http://httpbin.org/get').read()
assert_httpbin_responses_equal(body2, new_body2)
class TestHttps(unittest.TestCase):
def tearDown(self):
try:
os.remove(TEST_CASSETTE_FILE)
except OSError:
pass
def test_response_code(self):
code = urllib2.urlopen('https://httpbin.org/').getcode()
with vcr.use_cassette(TEST_CASSETTE_FILE):
self.assertEqual(code, urllib2.urlopen('https://httpbin.org/').getcode())
self.assertEqual(code, urllib2.urlopen('https://httpbin.org/').getcode())
def test_response_body(self):
body = urllib2.urlopen('https://httpbin.org/').read()
with vcr.use_cassette(TEST_CASSETTE_FILE):
self.assertEqual(body, urllib2.urlopen('https://httpbin.org/').read())
self.assertEqual(body, urllib2.urlopen('https://httpbin.org/').read())
def test_response_headers(self):
with vcr.use_cassette(TEST_CASSETTE_FILE):
headers = urllib2.urlopen('https://httpbin.org/').info().items()
self.assertEqual(headers, urllib2.urlopen('https://httpbin.org/').info().items())
def test_get_data(self):
TEST_DATA = urlencode({'some': 1, 'data': 'here'})
with vcr.use_cassette(TEST_CASSETTE_FILE):
body = urllib2.urlopen('https://httpbin.org/get?' + TEST_DATA).read()
self.assertEqual(body, urllib2.urlopen('https://httpbin.org/get?' + TEST_DATA).read())
def test_post_data(self):
TEST_DATA = urlencode({'some': 1, 'data': 'here'})
with vcr.use_cassette(TEST_CASSETTE_FILE):
body = urllib2.urlopen('https://httpbin.org/post', TEST_DATA).read()
self.assertEqual(body, urllib2.urlopen('https://httpbin.org/post', TEST_DATA).read())
def test_post_unicode(self):
TEST_DATA = urlencode({'snowman': u''.encode('utf-8')})
with vcr.use_cassette(TEST_CASSETTE_FILE):
body = urllib2.urlopen('https://httpbin.org/post', TEST_DATA).read()
self.assertEqual(body, urllib2.urlopen('https://httpbin.org/post', TEST_DATA).read())
class TestCassette(unittest.TestCase):
def test_serialize_cassette(self):
c1 = Cassette()
c1.requests = ['a', 'b', 'c']
c1.responses = ['d', 'e', 'f']
ser = c1.serialize()
c2 = Cassette(ser)
self.assertEqual(c1.requests, c2.requests)
self.assertEqual(c1.responses, c2.responses)
class TestCassette(TestVCR):
'''We should be able to save a cassette'''
fixtures = os.path.join('tests', 'fixtures', 'basic')
def test_nonexistent_directory(self):
'''If we load a cassette in a nonexistent directory, it can save ok'''
self.assertFalse(os.path.exists(self.fixture('nonexistent')))
with vcr.use_cassette(self.fixture('nonexistent', 'cass.yaml')):
urllib2.urlopen('http://httpbin.org/').read()
# This should have made the file and the directory
self.assertTrue(
os.path.exists(self.fixture('nonexistent', 'cass.yaml')))
if __name__ == '__main__':
unittest.main()
def test_unpatch(self):
'''Ensure that our cassette gets unpatched when we're done'''
with vcr.use_cassette(self.fixture('unpatch.yaml')) as cass:
urllib2.urlopen('http://httpbin.org/').read()
# Make the same requests, and assert that we haven't served any more
# requests out of cache
urllib2.urlopen('http://httpbin.org/').read()
self.assertEqual(len(cass.cached()), 0)

View File

@@ -1,110 +1,163 @@
# coding=utf-8
import os
import unittest
import vcr
import pytest
from utils import assert_httpbin_responses_equal
'''Test requests' interaction with vcr'''
# coding=utf-8
# Internal imports
import vcr
from .common import TestVCR
import os
import pytest
requests = pytest.importorskip("requests")
TEST_CASSETTE_FILE = 'cassettes/test_req.yaml'
class TestRequestsGet(unittest.TestCase):
class TestRequestsBase(TestVCR):
'''Some utility for running Requests tests'''
fixtures = os.path.join('tests', 'fixtures', 'requests')
def setUp(self):
self.unmolested_response = requests.get('http://httpbin.org/get')
with vcr.use_cassette(TEST_CASSETTE_FILE):
self.initial_response = requests.get('http://httpbin.org/get')
self.cached_response = requests.get('http://httpbin.org/get')
class TestHTTPRequests(TestRequestsBase):
'''Some tests using requests and http'''
scheme = 'http'
def test_status_code(self):
'''Ensure that we can read the status code'''
url = self.scheme + '://httpbin.org/'
with vcr.use_cassette(self.fixture('atts.yaml')) as cass:
# Ensure that this is empty to begin with
self.assertEqual(len(cass), 0)
self.assertEqual(len(cass.cached()), 0)
self.assertEqual(
requests.get(url).status_code,
requests.get(url).status_code)
# Ensure that we've now cached a single response
self.assertEqual(len(cass), 1)
self.assertEqual(len(cass.cached()), 1)
def test_headers(self):
'''Ensure that we can read the headers back'''
url = self.scheme + '://httpbin.org/'
with vcr.use_cassette(self.fixture('headers.yaml')) as cass:
# Ensure that this is empty to begin with
self.assertEqual(len(cass), 0)
self.assertEqual(len(cass.cached()), 0)
self.assertEqual(
requests.get(url).headers,
requests.get(url).headers)
# Ensure that we've now cached a single response
self.assertEqual(len(cass), 1)
self.assertEqual(len(cass.cached()), 1)
def test_body(self):
'''Ensure the responses are all identical enough'''
url = self.scheme + '://httpbin.org/bytes/1024'
with vcr.use_cassette(self.fixture('body.yaml')) as cass:
# Ensure that this is empty to begin with
self.assertEqual(len(cass), 0)
self.assertEqual(len(cass.cached()), 0)
self.assertEqual(
requests.get(url).content,
requests.get(url).content)
# Ensure that we've now cached a single response
self.assertEqual(len(cass), 1)
self.assertEqual(len(cass.cached()), 1)
def test_auth(self):
'''Ensure that we can handle basic auth'''
auth = ('user', 'passwd')
url = self.scheme + '://httpbin.org/basic-auth/user/passwd'
with vcr.use_cassette(self.fixture('auth.yaml')) as cass:
# Ensure that this is empty to begin with
self.assertEqual(len(cass), 0)
self.assertEqual(len(cass.cached()), 0)
one = requests.get(url, auth=auth)
two = requests.get(url, auth=auth)
self.assertEqual(one.content, two.content)
self.assertEqual(one.status_code, two.status_code)
# Ensure that we've now cached a single response
self.assertEqual(len(cass), 1)
self.assertEqual(len(cass.cached()), 1)
def test_auth_failed(self):
'''Ensure that we can save failed auth statuses'''
auth = ('user', 'wrongwrongwrong')
url = self.scheme + '://httpbin.org/basic-auth/user/passwd'
with vcr.use_cassette(self.fixture('auth-failed.yaml')) as cass:
# Ensure that this is empty to begin with
self.assertEqual(len(cass), 0)
self.assertEqual(len(cass.cached()), 0)
one = requests.get(url, auth=auth)
two = requests.get(url, auth=auth)
self.assertEqual(one.content, two.content)
self.assertEqual(one.status_code, two.status_code)
self.assertNotEqual(one.status_code, 200)
# Ensure that we've now cached a single response
self.assertEqual(len(cass), 1)
self.assertEqual(len(cass.cached()), 1)
def test_post(self):
'''Ensure that we can post and cache the results'''
data = {'key1': 'value1', 'key2': 'value2'}
url = self.scheme + '://httpbin.org/post'
with vcr.use_cassette(self.fixture('redirect.yaml')) as cass:
# Ensure that this is empty to begin with
self.assertEqual(len(cass), 0)
self.assertEqual(len(cass.cached()), 0)
self.assertEqual(
requests.post(url, data).content,
requests.post(url, data).content)
# Ensure that we've now cached a single response
self.assertEqual(len(cass), 1)
self.assertEqual(len(cass.cached()), 1)
def test_redirects(self):
'''Ensure that we can handle redirects'''
url = self.scheme + '://httpbin.org/redirect-to?url=bytes/1024'
with vcr.use_cassette(self.fixture('redirect.yaml')) as cass:
# Ensure that this is empty to begin with
self.assertEqual(len(cass), 0)
self.assertEqual(len(cass.cached()), 0)
self.assertEqual(
requests.get(url).content,
requests.get(url).content)
# Ensure that we've now cached /two/ responses. One for the redirect
# and one for the final fetch
self.assertEqual(len(cass), 2)
self.assertEqual(len(cass.cached()), 2)
class TestHTTPSRequests(TestHTTPRequests):
'''Same as above, now in https'''
scheme = 'https'
def test_cross_scheme(self):
'''Ensure that requests between schemes are treated separately'''
# First fetch a url under https, and then again under https and then
# ensure that we haven't served anything out of cache, and we have two
# requests / response pairs in the cassette
with vcr.use_cassette(self.fixture('cross_scheme.yaml')) as cass:
requests.get('https://httpbin.org/')
requests.get('http://httpbin.org/')
self.assertEqual(len(cass), 2)
self.assertEqual(len(cass.cached()), 0)
class TestWild(TestRequestsBase):
'''Test some examples from the wild'''
fixtures = os.path.join('tests', 'fixtures', 'wild')
def tearDown(self):
try:
os.remove(TEST_CASSETTE_FILE)
except OSError:
pass
# No deleting our directory, and ensure that it exists
self.assertTrue(os.path.exists(self.fixture()))
def test_initial_response_code(self):
self.assertEqual(self.unmolested_response.status_code, self.initial_response.status_code)
def test_cached_response_code(self):
self.assertEqual(self.unmolested_response.status_code, self.cached_response.status_code)
def test_initial_response_headers(self):
self.assertEqual(self.unmolested_response.headers['content-type'], self.initial_response.headers['content-type'])
def test_cached_response_headers(self):
self.assertEqual(self.unmolested_response.headers['content-type'], self.cached_response.headers['content-type'])
def test_initial_response_text(self):
assert_httpbin_responses_equal(self.unmolested_response.text, self.initial_response.text)
def test_cached_response_text(self):
assert_httpbin_responses_equal(self.unmolested_response.text, self.cached_response.text)
class TestRequestsAuth(unittest.TestCase):
def setUp(self):
self.unmolested_response = requests.get('https://httpbin.org/basic-auth/user/passwd', auth=('user', 'passwd'))
with vcr.use_cassette(TEST_CASSETTE_FILE):
self.initial_response = requests.get('https://httpbin.org/basic-auth/user/passwd', auth=('user', 'passwd'))
self.cached_response = requests.get('https://httpbin.org/basic-auth/user/passwd', auth=('user', 'passwd'))
def tearDown(self):
try:
os.remove(TEST_CASSETTE_FILE)
except OSError:
pass
def test_initial_response_code(self):
self.assertEqual(self.unmolested_response.status_code, self.initial_response.status_code)
def test_cached_response_code(self):
self.assertEqual(self.unmolested_response.status_code, self.cached_response.status_code)
def test_cached_response_auth_can_fail(self):
auth_fail_cached = requests.get('https://httpbin.org/basic-auth/user/passwd', auth=('user', 'passwdzzz'))
self.assertNotEqual(self.unmolested_response.status_code, auth_fail_cached.status_code)
class TestRequestsPost(unittest.TestCase):
def setUp(self):
payload = {'key1': 'value1', 'key2': 'value2'}
self.unmolested_response = requests.post('http://httpbin.org/post', payload)
with vcr.use_cassette(TEST_CASSETTE_FILE):
self.initial_response = requests.post('http://httpbin.org/post', payload)
self.cached_response = requests.post('http://httpbin.org/post', payload)
def tearDown(self):
try:
os.remove(TEST_CASSETTE_FILE)
except OSError:
pass
def test_initial_post_response_text(self):
assert_httpbin_responses_equal(self.unmolested_response.text, self.initial_response.text)
def test_cached_post_response_text(self):
assert_httpbin_responses_equal(self.unmolested_response.text, self.cached_response.text)
class TestRequestsHTTPS(unittest.TestCase):
maxDiff = None
def setUp(self):
self.unmolested_response = requests.get('https://httpbin.org/get')
with vcr.use_cassette(TEST_CASSETTE_FILE):
self.initial_response = requests.get('https://httpbin.org/get')
self.cached_response = requests.get('https://httpbin.org/get')
def tearDown(self):
try:
os.remove(TEST_CASSETTE_FILE)
except OSError:
pass
def test_initial_https_response_text(self):
assert_httpbin_responses_equal(self.unmolested_response.text, self.initial_response.text)
def test_cached_https_response_text(self):
assert_httpbin_responses_equal(self.unmolested_response.text, self.cached_response.text)
def test_domain_redirect(self):
'''Ensure that redirects across domains are considered unique'''
# In this example, seomoz.org redirects to moz.com, and if those
# requests are considered identical, then we'll be stuck in a redirect
# loop.
url = 'http://seomoz.org/'
with vcr.use_cassette(self.fixture('domain_redirect.yaml')) as cass:
requests.get(url, headers={'User-Agent': 'vcrpy-test'})
# Ensure that we've now served two responses. One for the original
# redirect, and a second for the actual fetch
self.assertEqual(len(cass.cached()), 2)

141
tests/test_urllib2.py Normal file
View File

@@ -0,0 +1,141 @@
'''Integration tests with urllib2'''
# coding=utf-8
# Internal imports
import vcr
from .common import TestVCR
# Testing urllib2 fetching
import os
import urllib2
from urllib import urlencode
class TestUrllib2(TestVCR):
'''Base class for tests for urllib2'''
fixtures = os.path.join('tests', 'fixtures', 'urllib2')
class TestUrllib2Http(TestUrllib2):
'''Tests for integration with urllib2'''
scheme = 'http'
def test_response_code(self):
'''Ensure we can read a response code from a fetch'''
url = self.scheme + '://httpbin.org/'
with vcr.use_cassette(self.fixture('atts.yaml')) as cass:
# Ensure that this is empty to begin with
self.assertEqual(len(cass), 0)
self.assertEqual(len(cass.cached()), 0)
self.assertEqual(
urllib2.urlopen(url).getcode(),
urllib2.urlopen(url).getcode())
# Ensure that we've now cached a single response
self.assertEqual(len(cass), 1)
self.assertEqual(len(cass.cached()), 1)
def test_random_body(self):
'''Ensure we can read the content, and that it's served from cache'''
url = self.scheme + '://httpbin.org/bytes/1024'
with vcr.use_cassette(self.fixture('body.yaml')) as cass:
# Ensure that this is empty to begin with
self.assertEqual(len(cass), 0)
self.assertEqual(len(cass.cached()), 0)
self.assertEqual(
urllib2.urlopen(url).read(),
urllib2.urlopen(url).read())
# Ensure that we've now cached a single response
self.assertEqual(len(cass), 1)
self.assertEqual(len(cass.cached()), 1)
def test_response_headers(self):
'''Ensure we can get information from the response'''
url = self.scheme + '://httpbin.org/'
with vcr.use_cassette(self.fixture('headers.yaml')) as cass:
# Ensure that this is empty to begin with
self.assertEqual(len(cass), 0)
self.assertEqual(len(cass.cached()), 0)
self.assertEqual(
urllib2.urlopen(url).info().items(),
urllib2.urlopen(url).info().items())
# Ensure that we've now cached a single response
self.assertEqual(len(cass), 1)
def test_multiple_requests(self):
'''Ensure that we can cache multiple requests'''
urls = [
self.scheme + '://httpbin.org/',
self.scheme + '://httpbin.org/get',
self.scheme + '://httpbin.org/bytes/1024'
]
with vcr.use_cassette(self.fixture('multiple.yaml')) as cass:
for index in range(len(urls)):
url = urls[index]
self.assertEqual(len(cass), index)
self.assertEqual(len(cass.cached()), index)
self.assertEqual(
urllib2.urlopen(url).read(),
urllib2.urlopen(url).read())
self.assertEqual(len(cass), index + 1)
self.assertEqual(len(cass.cached()), index + 1)
def test_get_data(self):
'''Ensure that it works with query data'''
data = urlencode({'some': 1, 'data': 'here'})
url = self.scheme + '://httpbin.org/get?' + data
with vcr.use_cassette(self.fixture('get_data.yaml')) as cass:
# Ensure that this is empty to begin with
self.assertEqual(len(cass), 0)
self.assertEqual(len(cass.cached()), 0)
self.assertEqual(
urllib2.urlopen(url).read(),
urllib2.urlopen(url).read())
# Ensure that we've now cached a single response
self.assertEqual(len(cass), 1)
self.assertEqual(len(cass.cached()), 1)
def test_post_data(self):
'''Ensure that it works when posting data'''
data = urlencode({'some': 1, 'data': 'here'})
url = self.scheme + '://httpbin.org/post'
with vcr.use_cassette(self.fixture('post_data.yaml')) as cass:
# Ensure that this is empty to begin with
self.assertEqual(len(cass), 0)
self.assertEqual(len(cass.cached()), 0)
self.assertEqual(
urllib2.urlopen(url, data).read(),
urllib2.urlopen(url, data).read())
# Ensure that we've now cached a single response
self.assertEqual(len(cass), 1)
self.assertEqual(len(cass.cached()), 1)
def test_post_unicode_data(self):
'''Ensure that it works when posting unicode data'''
data = urlencode({'snowman': u''.encode('utf-8')})
url = self.scheme + '://httpbin.org/post'
with vcr.use_cassette(self.fixture('post_data.yaml')) as cass:
# Ensure that this is empty to begin with
self.assertEqual(len(cass), 0)
self.assertEqual(len(cass.cached()), 0)
self.assertEqual(
urllib2.urlopen(url, data).read(),
urllib2.urlopen(url, data).read())
# Ensure that we've now cached a single response
self.assertEqual(len(cass), 1)
self.assertEqual(len(cass.cached()), 1)
class TestUrllib2Https(TestUrllib2Http):
'''Similar tests but for http status codes'''
scheme = 'https'
def test_cross_scheme(self):
'''Ensure that requests between schemes are treated separately'''
# First fetch a url under https, and then again under https and then
# ensure that we haven't served anything out of cache, and we have two
# requests / response pairs in the cassette
with vcr.use_cassette(self.fixture('cross_scheme.yaml')) as cass:
urllib2.urlopen('https://httpbin.org/')
urllib2.urlopen('http://httpbin.org/')
self.assertEqual(len(cass), 2)
self.assertEqual(len(cass.cached()), 0)