Skip to content

Commit 4f3b980

Browse files
committed
Merge pull request #11 from alexcouper/alex/check-post
Alex/check post
2 parents c13a464 + e657e8f commit 4f3b980

File tree

4 files changed

+171
-20
lines changed

4 files changed

+171
-20
lines changed

abe/mocks.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
import json
2+
import warnings
23

34
from supermutes.dot import dotify
45

56

67
class AbeMock(object):
78

8-
def __init__(self, filename):
9+
def __init__(self, data):
910
"""
1011
Initialise an ABE mock from data.
1112
12-
filename is expected to be the path to an ABE file.
13-
1413
"""
15-
with open(filename, 'r') as f:
16-
data = json.load(f)
14+
if not isinstance(data, dict):
15+
msg = ('Instanciating an AbeMock by filename is deprecated and '
16+
'will be removed in an upcoming release. '
17+
'Use AbeMock.from_filename instead'.format(data))
18+
warnings.warn(msg, DeprecationWarning)
19+
with open(data, 'r') as f:
20+
data = json.load(f)
1721

1822
# map JSON fields to attributes
1923
self.__dict__ = data
@@ -25,6 +29,18 @@ def __init__(self, filename):
2529
self._feed_inherited_fields(value, 'request', ['url', 'method'])
2630
self.examples[key] = dotify(value)
2731

32+
@classmethod
33+
def from_filename(cls, filename):
34+
"""
35+
Initialise an ABE mock from a spec file.
36+
37+
filename is expected to be the path to an ABE file.
38+
39+
"""
40+
with open(filename, 'r') as f:
41+
data = json.load(f)
42+
return AbeMock(data)
43+
2844
def _feed_inherited_fields(self, value, where, inheritable):
2945
"""
3046
If missing in request or response, some fields are inherited.

abe/unittest.py

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ def load_sample(self, sample_path):
2121
"""
2222
Load a sample file into an AbeMock object.
2323
"""
24-
sample_file = os.path.join(self.samples_root, sample_path)
25-
return AbeMock(sample_file)
24+
sample_filename = os.path.join(self.samples_root, sample_path)
25+
return AbeMock.from_filename(sample_filename)
2626

2727
def get_sample_request(self, path, label):
2828
"""
@@ -107,16 +107,39 @@ def assert_headers_contain(self, response_data, spec_data):
107107
"header {0}".format(expected_header)
108108
)
109109

110-
def assert_matches_sample(self, path, label, url, response):
110+
def assert_matches_request(self, sample_request, wsgi_request):
111+
"""
112+
Check that the sample request and wsgi request match.
113+
"""
114+
self.assertEqual(wsgi_request.META['PATH_INFO'], sample_request['url'])
115+
self.assertEqual(wsgi_request.META['REQUEST_METHOD'],
116+
sample_request['method'])
117+
118+
if 'headers' in sample_request:
119+
self.assert_headers_contain(
120+
wsgi_request.META, sample_request['headers']
121+
)
122+
123+
if 'body' in sample_request:
124+
self.assert_data_equal(wsgi_request.POST, sample_request['body'])
125+
126+
def assert_matches_response(self, sample_response, wsgi_response):
127+
"""
128+
Check that the sample response and wsgi response match.
129+
"""
130+
self.assertEqual(wsgi_response.status_code, sample_response.status)
131+
if 'body' in sample_response:
132+
response_parsed = wsgi_response.data
133+
self.assert_data_equal(response_parsed, sample_response.body)
134+
135+
def assert_matches_sample(self, path, label, response):
111136
"""
112137
Check a URL and response against a sample.
113138
114139
:param sample_name:
115140
The name of the sample file, e.g. 'query.json'
116141
:param label:
117142
The label for a specific sample request/response, e.g. 'OK'
118-
:param url:
119-
The actual URL we want to compare with the sample
120143
:param response:
121144
The actual API response we want to match with the sample.
122145
It is assumed to be a Django Rest Framework response object
@@ -125,13 +148,5 @@ def assert_matches_sample(self, path, label, url, response):
125148
sample_request = sample.examples[label].request
126149
sample_response = sample.examples[label].response
127150

128-
self.assertEqual(url, sample_request.url)
129-
if 'headers' in sample_request:
130-
self.assert_headers_contain(
131-
response.request, sample_request.headers
132-
)
133-
134-
self.assertEqual(response.status_code, sample_response.status)
135-
if 'body' in sample_response:
136-
response_parsed = response.data
137-
self.assert_data_equal(response_parsed, sample_response.body)
151+
self.assert_matches_response(sample_response, response)
152+
self.assert_matches_request(sample_request, response.wsgi_request)

tests/data/sample.json

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"description": "Retrieve profile of logged in user",
3+
"url": "/accounts/me",
4+
"method": "GET",
5+
"examples": {
6+
"OK": {
7+
"request": {
8+
"url": "/accounts/me"
9+
},
10+
"response": {
11+
"status": 200,
12+
"body": {
13+
"id": 1,
14+
"username": "user-0",
15+
"first_name": "",
16+
"last_name": "",
17+
"email": "[email protected]"
18+
}
19+
}
20+
},
21+
"unauthenticated": {
22+
"description": "I am not logged in",
23+
"request": {
24+
"url": "/accounts/me"
25+
},
26+
"response": {
27+
"status": 403,
28+
"body": {
29+
"detail": "Authentication credentials were not provided."
30+
}
31+
}
32+
}
33+
}
34+
}

tests/test_assertions.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1+
from os.path import abspath, dirname, join
12
from unittest import TestCase
3+
import warnings
24

5+
from mock import Mock
6+
7+
from abe.mocks import AbeMock
38
from abe.unittest import AbeTestMixin
49

10+
DATA_DIR = join(dirname(abspath(__file__)), 'data')
11+
512

613
class TestDataListEqual(TestCase, AbeTestMixin):
714

@@ -71,3 +78,82 @@ def test_requires_key_and_value_match(self):
7178
{'HTTP_THIS_CUSTOM': 'Foo'},
7279
{'This-Custom-Header': 'Foo'},
7380
)
81+
82+
83+
class TestAssertMatchesRequest(TestCase, AbeTestMixin):
84+
85+
def setUp(self):
86+
abe_mock = AbeMock({
87+
"method": "POST",
88+
"url": "/resource/",
89+
"examples": {
90+
"OK": {
91+
"request": {
92+
"queryParams": {},
93+
"body": {
94+
"name": "My Resource"
95+
},
96+
"headers": {
97+
"This-Custom-Header": 'Foo'
98+
}
99+
}
100+
}
101+
}
102+
})
103+
self.sample_request = abe_mock.examples['OK'].request
104+
105+
self.mock_wsgi_request = Mock(
106+
POST={'name': 'My Resource'},
107+
META={
108+
'HTTP_THIS_CUSTOM_HEADER': 'Foo',
109+
'REQUEST_METHOD': 'POST',
110+
'PATH_INFO': '/resource/'
111+
},
112+
)
113+
114+
def test_success_case(self):
115+
self.assert_matches_request(
116+
self.sample_request, self.mock_wsgi_request
117+
)
118+
119+
def test_assertion_error_if_url_mismatch(self):
120+
self.mock_wsgi_request.META['PATH_INFO'] = '/resourceoops/'
121+
122+
with self.assertRaises(AssertionError):
123+
self.assert_matches_request(
124+
self.sample_request, self.mock_wsgi_request
125+
)
126+
127+
def test_assertion_error_if_method_mismatch(self):
128+
self.mock_wsgi_request.META['REQUEST_METHOD'] = 'PATCH'
129+
130+
with self.assertRaises(AssertionError):
131+
self.assert_matches_request(
132+
self.sample_request, self.mock_wsgi_request
133+
)
134+
135+
def test_assertion_error_if_post_data_mismatch(self):
136+
self.mock_wsgi_request.POST = {'a': 1}
137+
138+
with self.assertRaises(AssertionError):
139+
self.assert_matches_request(
140+
self.sample_request, self.mock_wsgi_request
141+
)
142+
143+
144+
class TestFilenameInstantiation(TestCase):
145+
146+
def setUp(self):
147+
self.filename = join(DATA_DIR, 'sample.json')
148+
149+
def test_can_still_use_deprecated_instantiation(self):
150+
with warnings.catch_warnings(record=True) as w:
151+
warnings.simplefilter("always")
152+
153+
mock = AbeMock(self.filename)
154+
155+
self.assertEqual(len(w), 1)
156+
self.assertTrue(issubclass(w[-1].category, DeprecationWarning))
157+
158+
def test_from_filename(self):
159+
mock = AbeMock.from_filename(self.filename)

0 commit comments

Comments
 (0)