【问题标题】:Problem with dict/json payload in Flask tests - Object of type bytes is not JSON serializableFlask 测试中的 dict/json 有效负载问题 - 字节类型的对象不是 JSON 可序列化的
【发布时间】:2020-08-19 15:12:47
【问题描述】:

使用 Flask RestX,我有一个端点,它将请求有效负载的整个值设置为 Redis 数据库中的一个键。使用 Swagger 和 Postman,我可以确认端点工作正常。

但是当我尝试测试它时,我得到了一个错误。

tray_info_api.py

def create_tray_info_api(api: Api, db: AsyncGetSetDatabase):
    tray_info_endpoint = api.namespace(
        'trayinfo', description='APIs to Send tray information to the Front End'
    )

    wild = fields.Wildcard(fields.String)

    well_meta = api.model('Well Metadata', {
        'label': fields.String,
        'type': fields.String,
        'value': wild
    })

    well = api.model('Well', {
        'metadata': fields.List(fields.Nested(well_meta)),
        'status': fields.String(enum=('ready', 'sampled'), required=True)
        # and some other fields with string/integer/datetime types
    })

    tray = api.model('Well Tray', {
        'rows_count': fields.Integer(required=True),
        'columns_count': fields.Integer(required=True),
        'wells': fields.List(fields.Nested(well), required=True)
    })

    @tray_info_endpoint.route('/')
    class TrayInfoEndpoint(Resource):

        @tray_info_endpoint.expect(tray, validate=False)
        def put(self):
            run(db.set('tray_info', request.data))
            return run(db.get('tray_info'))

test_info_endpoint.py

def test_put_info(app):
    info = {
        "rows_count": 0,
        "columns_count": 0,
        "date_loaded": "2020-08-19T14:11:29.320Z",
        "date_processed": "2020-08-19T14:11:29.320Z",
        # ... and all the other fields; this data is copy/pasted from a working Postman request
    }
    res = app.test_client().put('/trayinfo/', json=info)   
    data = json.loads(res.data)
    assert data == info

调试器在以res = app.test_client 开头的行停止测试,说“TypeError: Object of type bytes is not JSON serializable”。

如果我在 api 的 put 方法中放置一个断点并进入控制台,我看到 request.data 是我发送的数据,整个 JSON 上有 b' 前缀:

>>> request.data
b'{"columns_count": 0, "date_loaded": "2020-08-19T14:11:29.320Z", "date_processed": "2020-08-19T14:11:29.320Z", "rows_count": 0}'

我知道这表明它是字节,这似乎指向答案,除非这是问题所在,那么该应用程序不应该实际上不能在 Postman 中运行吗?我可以毫无错误地单步执行整个put 方法,似乎错误来自测试本身的行,这真的很奇怪。

我也尝试过res = app.test_client().put(url, data=info),这导致request.data 成为b''(一个空的有效负载),所以这是不对的。

【问题讨论】:

    标签: python flask


    【解决方案1】:

    事先将您的字节解码为 un​​icode

    import json
    
    data1 = {
      "field":b"bytes"
    }
    json.dumps(data1) # ERROR!!!
    
    data2 = {
      "field":b"bytes".decode("utf8")
    }
    json.dumps(data2) # ALL Good now its a string
    

    如果您提前转储 json,您可以包含一个“default_encoder”来处理无法正常编码的对象

    def encoder(obj):
        if isinstance(obj,bytes):
           return obj.decode("utf8")
        return obj
    
    json.dumps(data1,default=encoder)
    

    【讨论】:

    • 我不清楚在我的具体情况下如何使用它。请注意,我已经更新了我的问题以显示整个 JSON 是字节,或者字符串化的 JSON 是字节,或者......无论它是什么,你都可以在我的问题中看到。
    • 您需要将字节的任何字段更改为 unicode ... 因为您只显示 2 个整数字段,所以很难完全针对您的情况提供帮助...
    • 好点!我添加了一些日期时间示例,这些示例实际上似乎是字符串。我刚刚尝试了"date_loaded": b"2020-08-19T14:11:29.320Z".decode('utf8'),结果相同。请注意,在我的编辑中,putrequest.data 内部是 b'{"columns_count": 0" #etc }',所以我认为问题不在于构建字典。
    • 那些是字符串而不是字节......如果我复制并粘贴该字典,它将 json 编码没问题
    • request.data 怎么样?无论如何,您可以编辑答案以直接涉及我的代码,而不是在什么是什么的定义上来回走动?我仍然无法将你所说的应用于我的问题。
    【解决方案2】:

    感谢 Joran Beasley 为我指明了正确的方向。我终于想出了如何将他的建议应用于 json 解码,但这不是我原来帖子中的任何内容。

    您可以看到create_tray_info_api 方法采用数据库(依赖注入)。在真实的应用程序中,这是使用 Redis 数据库创建的,但在测试中我使用了一个夹具来注入一个简单的模拟数据库,其 set 方法是:

    async def set(self, key, value):
        self.db[key] = value
    

    request.data,正如我所指出的,是一个包含 json 信息的字节对象,我的 put 方法将其写入数据库。我猜 Redis 会以某种方式自己处理编码/解码(或者浏览器/swagger/postman 可能会这样做),这就是它在实际应用中工作的原因。

    然而,模拟数据库只是按原样写入和读取。所以我需要在保存到数据库时解码字节。当然,这只适用于字节,所以我必须确保整数、字符串等正常通过:

    async def set(self, key, value):
        self.db[key] = value.decode('utf8') if isinstance(value, bytes) else value
    

    那么在测试中,data作为一个字符串返回,所以我们需要将它转换回json来断言它的相等性:

    assert info == json.loads(data)
    

    【讨论】:

      猜你喜欢
      • 2022-01-26
      • 2021-09-24
      • 2020-09-05
      • 2017-11-24
      • 1970-01-01
      • 2019-03-05
      • 2019-07-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多