1
1
import os
2
2
3
3
from .mocks import AbeMock
4
- from .utils import to_unicode
4
+ from .utils import normalize , subkeys
5
5
6
6
7
7
class AbeTestMixin (object ):
@@ -33,53 +33,71 @@ def get_sample_request(self, path, label):
33
33
sample_request = sample .examples [label ].request
34
34
return sample_request .body
35
35
36
- def assert_data_equal (self , data1 , data2 ):
36
+ def assert_item_matches (self , real , sample ):
37
+ """
38
+ A primitive value matches the sample.
39
+
40
+ If the sample represents a parameter, then do simple pattern matching.
41
+
42
+ """
43
+ real = normalize (real )
44
+ sample = normalize (sample )
45
+ self .assertEqual (real , sample )
46
+
47
+ def assert_data_equal (self , real , sample , non_strict = None ):
37
48
"""
38
49
Two elements are recursively equal
50
+
51
+ :param non_strict:
52
+ Names of fields to match non-strictly. In current implementation,
53
+ only check for field presence.
39
54
"""
55
+ non_strict = non_strict or []
40
56
try :
41
- if isinstance (data1 , list ):
42
- self .assertIsInstance (data2 , list )
43
- self .assert_data_list_equal (data1 , data2 )
44
- elif isinstance (data1 , dict ):
45
- self .assertIsInstance (data2 , dict )
46
- self .assert_data_dict_equal (data1 , data2 )
57
+ if isinstance (real , list ):
58
+ self .assertIsInstance (sample , list )
59
+ self .assert_data_list_equal (real , sample , non_strict )
60
+ elif isinstance (real , dict ):
61
+ self .assertIsInstance (sample , dict )
62
+ self .assert_data_dict_equal (real , sample , non_strict )
47
63
else :
48
- data1 = to_unicode (data1 )
49
- data2 = to_unicode (data2 )
50
- self .assertIsInstance (data2 , data1 .__class__ )
51
- self .assertEqual (data1 , data2 )
64
+ self .assert_item_matches (real , sample )
52
65
except AssertionError as exc :
53
- message = str (exc ) + '\n {}\n {}\n \n ' .format (data1 , data2 )
66
+ message = str (exc ) + '\n {}\n {}\n \n ' .format (real , sample )
54
67
raise type (exc )(message )
55
68
56
- def assert_data_dict_equal (self , data1 , data2 ):
69
+ def assert_data_dict_equal (self , real , sample , non_strict = None ):
57
70
"""
58
71
Two dicts are recursively equal without taking order into account
59
72
"""
73
+ non_strict = non_strict or []
60
74
self .assertEqual (
61
- len (data1 ), len (data2 ),
75
+ len (real ), len (sample ),
62
76
msg = 'Number of elements mismatch: {} != {}\n ' .format (
63
- data1 .keys (), data2 .keys ())
77
+ real .keys (), sample .keys ())
64
78
)
65
- for key in data1 :
66
- self .assertIn (key , data2 )
67
- self .assert_data_equal (data1 [key ], data2 [key ])
79
+ for key in sample :
80
+ self .assertIn (key , real )
81
+ if key not in non_strict :
82
+ inner_non_strict = subkeys (non_strict , key )
83
+ self .assert_data_equal (
84
+ real [key ], sample [key ], inner_non_strict )
68
85
69
- def assert_data_list_equal (self , data1 , data2 ):
86
+ def assert_data_list_equal (self , real , sample , non_strict = None ):
70
87
"""
71
88
Two lists are recursively equal, including ordering.
72
89
"""
90
+ non_strict = non_strict or []
73
91
self .assertEqual (
74
- len (data1 ), len (data2 ),
92
+ len (real ), len (sample ),
75
93
msg = 'Number of elements mismatch: {} {}' .format (
76
- data1 , data2 )
94
+ real , sample )
77
95
)
78
96
79
97
exceptions = []
80
- for element , element2 in zip (data1 , data2 ):
98
+ for real_item , sample_item in zip (real , sample ):
81
99
try :
82
- self .assert_data_equal (element , element2 )
100
+ self .assert_data_equal (real_item , sample_item , non_strict )
83
101
except AssertionError as exc :
84
102
exceptions .append (exc )
85
103
@@ -107,32 +125,44 @@ def assert_headers_contain(self, response_data, spec_data):
107
125
"header {0}" .format (expected_header )
108
126
)
109
127
110
- def assert_matches_request (self , sample_request , wsgi_request ):
128
+ def assert_matches_request (self , sample_request , wsgi_request ,
129
+ non_strict = None ):
111
130
"""
112
131
Check that the sample request and wsgi request match.
113
132
"""
114
- self .assertEqual (wsgi_request .META ['PATH_INFO' ], sample_request ['url' ])
115
- self .assertEqual (wsgi_request .META ['REQUEST_METHOD' ],
116
- sample_request ['method' ])
133
+ non_strict = non_strict or []
134
+
135
+ if 'url' not in non_strict :
136
+ self .assertEqual (wsgi_request .META ['PATH_INFO' ],
137
+ sample_request ['url' ])
138
+ if 'method' not in non_strict :
139
+ self .assertEqual (wsgi_request .META ['REQUEST_METHOD' ],
140
+ sample_request ['method' ])
117
141
118
- if 'headers' in sample_request :
142
+ if 'headers' in sample_request and 'headers' not in non_strict :
119
143
self .assert_headers_contain (
120
144
wsgi_request .META , sample_request ['headers' ]
121
145
)
122
146
123
- if 'body' in sample_request :
147
+ if 'body' in sample_request and 'body' not in non_strict :
124
148
self .assert_data_equal (wsgi_request .POST , sample_request ['body' ])
125
149
126
- def assert_matches_response (self , sample_response , wsgi_response ):
150
+ def assert_matches_response (self , sample_response , wsgi_response ,
151
+ non_strict = None ):
127
152
"""
128
153
Check that the sample response and wsgi response match.
129
154
"""
155
+ non_strict = non_strict or []
130
156
self .assertEqual (wsgi_response .status_code , sample_response .status )
131
157
if 'body' in sample_response :
132
158
response_parsed = wsgi_response .data
133
- self .assert_data_equal (response_parsed , sample_response .body )
159
+ self .assert_data_equal (
160
+ response_parsed , sample_response .body , non_strict )
134
161
135
- def assert_matches_sample (self , path , label , response ):
162
+ def assert_matches_sample (
163
+ self , path , label , response , non_strict_response = None ,
164
+ non_strict_request = None
165
+ ):
136
166
"""
137
167
Check a URL and response against a sample.
138
168
@@ -143,10 +173,19 @@ def assert_matches_sample(self, path, label, response):
143
173
:param response:
144
174
The actual API response we want to match with the sample.
145
175
It is assumed to be a Django Rest Framework response object
176
+ :param non_strict:
177
+ List of fields that will not be checked for strict matching.
178
+ You can use this to include server-generated fields whose exact
179
+ value you don't care about in your test, like ids, dates, etc.
146
180
"""
181
+ non_strict_response = non_strict_response or []
182
+ non_strict_request = non_strict_request or []
147
183
sample = self .load_sample (path )
148
184
sample_request = sample .examples [label ].request
149
185
sample_response = sample .examples [label ].response
150
186
151
- self .assert_matches_response (sample_response , response )
152
- self .assert_matches_request (sample_request , response .wsgi_request )
187
+ self .assert_matches_response (
188
+ sample_response , response , non_strict = non_strict_response )
189
+ self .assert_matches_request (
190
+ sample_request , response .wsgi_request ,
191
+ non_strict = non_strict_request )
0 commit comments