0
# Response Validation
1
2
Comprehensive validation functions for API responses including JWT validation, regex matching, schema validation, and content assertions using JMESPath expressions.
3
4
## Capabilities
5
6
### JWT Token Validation
7
8
Validates and decodes JWT tokens from API responses using the PyJWT library, returning decoded claims for use in subsequent test stages.
9
10
```python { .api }
11
def validate_jwt(
12
response: requests.Response,
13
jwt_key: str,
14
**kwargs
15
) -> Mapping[str, Box]:
16
"""
17
Validate and decode JWT token from response.
18
19
Parameters:
20
- response: HTTP response object containing JWT
21
- jwt_key: Key name in response JSON containing the JWT token
22
- **kwargs: Additional arguments passed to jwt.decode() (e.g., verify_signature=False)
23
24
Returns:
25
Mapping[str, Box]: Dictionary with 'jwt' key containing boxed JWT claims
26
27
Usage:
28
Can be used both for validation and to extract JWT claims for template variables
29
"""
30
```
31
32
**Usage Examples:**
33
34
```python
35
import requests
36
from tavern.helpers import validate_jwt
37
38
# Basic JWT validation
39
response = requests.get("https://api.example.com/auth")
40
jwt_data = validate_jwt(response, "access_token", verify_signature=False)
41
print(jwt_data["jwt"]["user_id"]) # Access decoded claims
42
43
# In YAML test with validation
44
# response:
45
# status_code: 200
46
# json:
47
# access_token: !anything
48
# validate:
49
# - function: tavern.helpers:validate_jwt
50
# extra_kwargs:
51
# jwt_key: access_token
52
# verify_signature: false
53
# save:
54
# json:
55
# token_claims: !jwt_claims access_token
56
```
57
58
### Regex Pattern Validation
59
60
Validates response content against regular expressions with support for header matching and JMESPath content extraction.
61
62
```python { .api }
63
def validate_regex(
64
response: requests.Response,
65
expression: str,
66
*,
67
header: Optional[str] = None,
68
in_jmespath: Optional[str] = None,
69
) -> dict[str, Box]:
70
"""
71
Validate response content against regex pattern.
72
73
Parameters:
74
- response: HTTP response object
75
- expression: Regular expression pattern to match
76
- header: Optional header name to match against instead of body
77
- in_jmespath: Optional JMESPath to extract content before regex matching
78
79
Returns:
80
dict[str, Box]: Dictionary with 'regex' key containing boxed capture groups
81
82
Raises:
83
BadSchemaError: If both header and in_jmespath are specified
84
RegexAccessError: If regex doesn't match or JMESPath extraction fails
85
"""
86
```
87
88
**Usage Examples:**
89
90
```python
91
from tavern.helpers import validate_regex
92
93
# Match against response body
94
result = validate_regex(response, r"User ID: (\d+)")
95
user_id = result["regex"]["1"] # First capture group
96
97
# Match against specific header
98
result = validate_regex(
99
response,
100
r"Bearer ([a-zA-Z0-9]+)",
101
header="Authorization"
102
)
103
token = result["regex"]["1"]
104
105
# Match against JMESPath extracted content
106
result = validate_regex(
107
response,
108
r"Error: (.+)",
109
in_jmespath="error.message"
110
)
111
error_msg = result["regex"]["1"]
112
113
# In YAML test
114
# response:
115
# status_code: 200
116
# validate:
117
# - function: tavern.helpers:validate_regex
118
# extra_kwargs:
119
# expression: "Order #([0-9]+) created"
120
# save:
121
# regex:
122
# order_id: "1" # First capture group
123
```
124
125
### Content Validation with JMESPath
126
127
Validates response content using JMESPath expressions with various comparison operators for complex assertions.
128
129
```python { .api }
130
def validate_content(
131
response: requests.Response,
132
comparisons: Iterable[dict]
133
) -> None:
134
"""
135
Validate response content using JMESPath expressions and operators.
136
137
Parameters:
138
- response: HTTP response object with JSON content
139
- comparisons: List of comparison dictionaries with keys:
140
- jmespath: JMESPath expression to extract data
141
- operator: Comparison operator (eq, ne, gt, lt, gte, lte, in, contains)
142
- expected: Expected value to compare against
143
144
Raises:
145
JMESError: If validation fails or JMESPath expression is invalid
146
"""
147
```
148
149
**Usage Examples:**
150
151
```python
152
from tavern.helpers import validate_content
153
154
# Single comparison
155
comparisons = [{
156
"jmespath": "users.length(@)",
157
"operator": "gt",
158
"expected": 0
159
}]
160
validate_content(response, comparisons)
161
162
# Multiple comparisons
163
comparisons = [
164
{
165
"jmespath": "status",
166
"operator": "eq",
167
"expected": "success"
168
},
169
{
170
"jmespath": "data.users[0].age",
171
"operator": "gte",
172
"expected": 18
173
},
174
{
175
"jmespath": "data.users[].name",
176
"operator": "contains",
177
"expected": "John"
178
}
179
]
180
validate_content(response, comparisons)
181
```
182
183
### JMESPath Query Matching
184
185
Executes JMESPath queries against parsed response data with optional value validation.
186
187
```python { .api }
188
def check_jmespath_match(
189
parsed_response,
190
query: str,
191
expected: Optional[str] = None
192
):
193
"""
194
Check JMESPath query against response data.
195
196
Parameters:
197
- parsed_response: Parsed response data (dict or list)
198
- query: JMESPath query expression
199
- expected: Optional expected value to match against query result
200
201
Returns:
202
Query result value
203
204
Raises:
205
JMESError: If query path not found or doesn't match expected value
206
"""
207
```
208
209
**Usage Examples:**
210
211
```python
212
from tavern.helpers import check_jmespath_match
213
214
# Check if path exists and return value
215
data = {"users": [{"name": "John", "age": 30}]}
216
result = check_jmespath_match(data, "users[0].name")
217
print(result) # "John"
218
219
# Check path exists with expected value
220
check_jmespath_match(data, "users[0].age", 30) # Passes
221
check_jmespath_match(data, "users.length(@)", 1) # Passes
222
223
# Check complex queries
224
check_jmespath_match(
225
data,
226
"users[?age > `25`].name",
227
["John"]
228
)
229
```
230
231
### Schema Validation
232
233
Validates response JSON against Pykwalify schemas for structural and type validation.
234
235
```python { .api }
236
def validate_pykwalify(
237
response: requests.Response,
238
schema: dict
239
) -> None:
240
"""
241
Validate response JSON against Pykwalify schema.
242
243
Parameters:
244
- response: HTTP response object with JSON content
245
- schema: Pykwalify schema dictionary
246
247
Raises:
248
BadSchemaError: If response is not JSON or doesn't match schema
249
"""
250
```
251
252
**Usage Examples:**
253
254
```python
255
from tavern.helpers import validate_pykwalify
256
257
# Define schema
258
schema = {
259
"type": "map",
260
"mapping": {
261
"status": {"type": "str", "required": True},
262
"data": {
263
"type": "map",
264
"mapping": {
265
"users": {
266
"type": "seq",
267
"sequence": [{
268
"type": "map",
269
"mapping": {
270
"id": {"type": "int"},
271
"name": {"type": "str"},
272
"email": {"type": "str", "pattern": r".+@.+\..+"}
273
}
274
}]
275
}
276
}
277
}
278
}
279
}
280
281
# Validate response
282
validate_pykwalify(response, schema)
283
```
284
285
### Exception Validation
286
287
Validates that API error responses match expected exception formats and status codes.
288
289
```python { .api }
290
def check_exception_raised(
291
response: requests.Response,
292
exception_location: str
293
) -> None:
294
"""
295
Validate response matches expected exception format.
296
297
Parameters:
298
- response: HTTP response object containing error
299
- exception_location: Entry point style location of exception class
300
(e.g., "myapp.exceptions:ValidationError")
301
302
Raises:
303
UnexpectedExceptionError: If response doesn't match expected exception format
304
"""
305
```
306
307
**Usage Examples:**
308
309
```python
310
from tavern.helpers import check_exception_raised
311
312
# Validate error response format
313
try:
314
response = requests.post("https://api.example.com/invalid")
315
check_exception_raised(response, "myapp.exceptions:ValidationError")
316
except UnexpectedExceptionError:
317
print("Error response format doesn't match expected exception")
318
319
# In YAML test
320
# response:
321
# status_code: 400
322
# validate:
323
# - function: tavern.helpers:check_exception_raised
324
# extra_kwargs:
325
# exception_location: "myapp.exceptions:ValidationError"
326
```
327
328
## YAML Integration Examples
329
330
### Using Validation Functions in Tests
331
332
```yaml
333
test_name: Comprehensive validation example
334
335
stages:
336
- name: Test with multiple validations
337
request:
338
url: https://api.example.com/data
339
method: GET
340
response:
341
status_code: 200
342
validate:
343
# JWT validation
344
- function: tavern.helpers:validate_jwt
345
extra_kwargs:
346
jwt_key: access_token
347
verify_signature: false
348
349
# Regex validation
350
- function: tavern.helpers:validate_regex
351
extra_kwargs:
352
expression: "Request ID: ([A-Z0-9-]+)"
353
header: "X-Request-ID"
354
355
# Content validation
356
- function: tavern.helpers:validate_content
357
extra_kwargs:
358
comparisons:
359
- jmespath: "data.items.length(@)"
360
operator: "gt"
361
expected: 0
362
- jmespath: "status"
363
operator: "eq"
364
expected: "success"
365
366
# Schema validation
367
- function: tavern.helpers:validate_pykwalify
368
extra_kwargs:
369
schema:
370
type: map
371
mapping:
372
status: {type: str}
373
data: {type: map}
374
375
save:
376
jwt:
377
user_id: user_id
378
regex:
379
request_id: "1"
380
```
381
382
## Types
383
384
```python { .api }
385
from typing import Optional, Mapping, Iterable
386
import requests
387
from box.box import Box
388
389
BadSchemaError = "tavern._core.exceptions.BadSchemaError"
390
RegexAccessError = "tavern._core.exceptions.RegexAccessError"
391
JMESError = "tavern._core.exceptions.JMESError"
392
UnexpectedExceptionError = "tavern._core.exceptions.UnexpectedExceptionError"
393
```