Closed pshokeen closed 8 years ago
@pshokeen Hi there! There are two ways to accomplish this with PyRestTest:
For examples and information, see the extensions guide
Doing a comparator extension will be simpler than a full validator extension, because you only need a function that takes two inputs - in your case, the python array from the jsonpath_mini extractor, and the expression to test with.
If you want a full validator, the parse function takes a configuration object (python, extracted from YAML) and then returns a configured instance of the validator.
Does that answer your question?
@svanoort Somewhat. Im have two prototypes for doing this right now.
Prototype 1:
Extension.py:
def json_array_contains(config):
dictArray = json.loads(config['body'])
keyList = config['keys']
value = config['value']
for item in dictArray:
flag = False
for key in keyList:
if value.lower() in str(item.get(key, [])).lower():
flag = True
break
if not flag:
return False
return True
VALIDATORS = {
'json_contains': json_array_contains
}
Test.yaml This is where im unsure of how to use this
Want to validate that each element in the json contains a key called username and has test as the substring of the value
- validators:
- json_contains: lambda(body, 'username', 'test')
The test before this simply does a rest call to an endpoint with some query parameters and the response body is a json array of objects. Im unsure of how to PASS in the response body to the validator. Because when i simply do body like this, it passes in the text "body"
{'body': 'body', 'keys': ['username'], 'value': 'test'}
And expectedly i get the exception
raise ValueError("No JSON object could be decoded")
ValueError: No JSON object could be decoded
Prorotype 2 is lambda expressions, but i'd rather understand this first.
@pshokeen That's a start but I there is some confusion about how extensions and tests work. You can't use a lambda in the YAML (it's not allowed to be fully executable). You'll have to parse something from the config for the validator to configure your lambda if you do that.
For a validator, what is actually invoked is the 'validate' function: def validate(self, body=None, headers=None, context=None). Your validator must have validate and parse functions
Here's one approach using comparators (hacky but ought to work):
def all_elements_contain_value_in_string(json_array, config):
key = config['key']
expected_val = config['value']
if not isinstance(json_array, dict):
return False
try:
for item in json_array:
val = item[key]
if expected_val no in val:
return False
return True
except KeyError:
return False
COMPARATOR = {
'all_elements_contain_value_in_string': all_elements_contain_value_in_string
}
Test syntax
- validators:
- compare: {jsonpath_mini: '.', comparator:'all_elements_contain_value_in_string', expected: {'key':'username', 'value': 'test'}}
This is the quick-and-dirty solution since you don't get the proper logging and failure reasons that validators provide.
Got it working. Thanks for the advice @svanoort
@svanoort https://github.com/svanoort/pyresttest/blob/master/sample_extension.py here, i just wanna try to use your sample validator "is_dict", i change "is_dict" to "is_notnull_list". def is_notnull_list(input): return input is not None then, i try to use it in this way:
Im trying to create a json array validator. In a nutshell, the problem is my web response is a straight up json array from a search query. And i have to validate a property on each object. For example, whether each object's username contains a substring value.
how would the parse function be used exactly.
Do i have to define a separate "contains" check for each time i want to do a comparison on a property in the json object, or can i use the existing extract_test/jsonpath_mini, contains comparators?