【问题标题】:Python: Comparing two JSON objects in pytestPython:比较 pytest 中的两个 JSON 对象
【发布时间】:2018-03-28 22:05:52
【问题描述】:

我有一个返回这个 JSON 响应的 API

{
    "message": "Staff name and password pair not match",
    "errors": {
        "resource": "Login",
        "field": "staff_authentication",
        "code": "invalid",
        "stack_trace": null
    }
}

使用 pytest,我想构建一个 JSON 对象的副本并确保它完全一样

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 表明 2 个对象不相等。

>       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 ===========================

如何使新创建的 JSON 对象与响应相同?

【问题讨论】:

    标签: python json pytest object-comparison


    【解决方案1】:

    如果您确实需要两个字典之间的字面值、值到值相等,比较它们的 json 序列化结果会更简单,否则您需要对 dicts 及其值进行一些递归比较

    注意:由于 python 中的 dicts 是未排序的集合,因此您需要将 sort_keys=True 传递给 json.dumps,有关详细信息,请参阅 this question

    【讨论】:

    • 感谢您的回答。 sort_keys=True 有必要吗?即使我把键弄乱了,比较仍然是正确的。
    • 如果你使用python3,很可能是由于dict的当前实现是保序的,但据我所知,它不是有意的,也没有包含在规范中,所以它可以工作,但从长远来看,我不会依赖这个。在 python2 中,我认为这不应该是相同的
    • Python3 已经承诺 dicts 将被排序,所以你可以依赖它。
    【解决方案2】:

    我没有将 JSON 响应转换为 Object,而是使用 json.loads() 将其转换为 Dictionary,然后进行比较。

    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
    

    这样,我不必担心 JSON 响应中的空格差异以及 JSON 结构的顺序。只需让 Python 的 Dictionary 比较函数检查是否相等。

    【讨论】:

      猜你喜欢
      • 2014-03-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-29
      • 2023-03-26
      • 2022-01-03
      • 2021-03-01
      相关资源
      最近更新 更多