I have an API that returns this JSON response
{
"message": "Staff name and password pair not match",
"errors": {
"resource": "Login",
"field": "staff_authentication",
"code": "invalid",
"stack_trace": null
}
}
Using pytest, I want to build a copy of the JSON object and make sure it is exactly the same
import pytest
import json
from collections import namedtuple
from flask import url_for
from myapp import create_app
@pytest.mark.usefixtures('client_class')
class TestAuth:
def test_login(self, client):
assert client.get(url_for('stafflogin')).status_code == 405
res = self._login(client, 'no_such_user', '123456')
assert res.status_code == 422
response_object = self._json2obj(res.data)
assert response_object.message == 'Staff name and password pair not match'
invalid_password_json = dict(message="Staff name and password pair not match",
errors=dict(
resource="Login",
code="invalid",
field="staff_authentication",
stack_trace=None,)
)
assert self._ordered(response_object) == self._ordered(invalid_password_json)
def _login(self, client, staff_name, staff_password):
return client.post('/login',
data=json.dumps(dict(staff_name=staff_name, staff_password=staff_password)),
content_type='application/json',
follow_redirects=True)
def _json_object_hook(self, d): return namedtuple('X', d.keys())(*d.values())
def _json2obj(self, data): return json.loads(data, object_hook=self._json_object_hook)
def _ordered(self, obj):
if isinstance(obj, dict):
return sorted((k, self._ordered(v)) for k, v in obj.items())
if isinstance(obj, list):
return sorted(self._ordered(x) for x in obj)
else:
return obj
pytest
shows that the 2 objects are unequal.
> assert self._ordered(response_object) == self._ordered(invalid_password_json)
E AssertionError: assert X(message='St...k_trace=None)) == [('errors', [(...r not match')]
E At index 0 diff: 'Staff name and password pair not match' != ('errors', [('code', 'invalid'), ('field', 'staff_authentication'), ('resource', 'Login'), ('stack_trace', None)])
E Full diff:
E - X(message='Staff name and password pair not match', errors=X(resource='Login', field='staff_authentication', code='invalid', stack_trace=None))
E + [('errors',
E + [('code', 'invalid'),
E + ('field', 'staff_authentication'),
E + ('resource', 'Login'),
E + ('stack_trace', None)]),
E + ('message', 'Staff name and password pair not match')]
tests/test_app.py:31: AssertionError
=========================== 1 failed in 0.22 seconds ===========================
How do I make the newly created JSON object to the same as the response?
Comparing json is quite simple, we can use '==' operator, Note: '==' and 'is' operator are not same, '==' operator is use to check equality of values , whereas 'is' operator is used to check reference equality, hence one should use '==' operator, 'is' operator will not give expected result.
Compare Two JSON Objects Containing a List Element. Similarly, we can also compare two JSON objects that contain a list element. It's important to know that two list elements are only compared as equal if they have the same values in the exact same order.
You can also directly compare two JSON files by specifying their urls in the GET parameters url1 and url2. Then you can visualize the differences between the two JSON documents. It highlights the elements which are different: Different value between the two JSON: highlight in red color.
Instead of converting the JSON response into Object, I use json.loads()
to convert it into a Dictionary, and compare them.
def test_login(self, client):
res = return client.post('/login',
data=json.dumps(dict(staff_name='no_such_user', staff_password='password')),
content_type='application/json',
follow_redirects=True)
assert res.status_code == 422
invalid_password_json = dict(message="Staff name and password pair not match",
errors=dict(
resource="Login",
code="invalid",
field="staff_authentication",
stack_trace=None,),
)
assert json.loads(res.data) == invalid_password_json
This way, I do not have to worry about whitespace differences in the JSON response, as well as ordering of the JSON structure. Simply let Python's Dictionary comparison function check for equality.
If you do indeed require literal, value-to-value equality between two doctionaries, it would be simpler to compare their json serialization results, otherwise you would need some recursive comparison of dicts and their values
Note: since dicts in python are unsorted collections, you would require passing sort_keys=True
to json.dumps
, see this question for more details
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With