Django pytest测试GraphQL上传:SimpleUploadedFile序列化错误修复
报错根本成因
- 错误根源是把SimpleUploadedFile文件对象直接放到了需要被JSON序列化的GraphQL普通请求参数里。
- 你代码里依赖的
client_queryfixture,默认实现是按照application/json格式构造请求:会把传入的input_data/variables字典整体丢给Python原生json模块做序列化生成请求体。而SimpleUploadedFile是Django自定义的文件类,不属于json模块支持的可序列化原生类型,序列化到这一步就会直接抛出not JSON serializable错误。 - 从协议层面讲,GraphQL的文件上传本身就不支持把文件内容塞到JSON请求体里,官方约定的文件上传必须走
multipart/form-data格式,要单独拆分文件块、配置变量和文件的映射关系,当前的传参方式从协议规范上就是不兼容的。
具体修复方案
- 改造测试client的逻辑,使其支持GraphQL multipart上传协议:不要无脑把所有参数都做JSON序列化,当检测到存在文件类型参数时,自动切换成multipart格式构造请求,严格按照规范拆分三个请求段:
- operations段:存储GraphQL查询语句、非文件类型的普通变量,文件对应的变量位置先设为null
- map段:建立上传文件和查询变量路径的对应关系
- 文件段:单独携带每个待上传的文件内容
- 把原来塞在
input_data里的SimpleUploadedFile对象抽离,不要让它进入JSON序列化流程,单独通过client的文件参数传入。 - 修正代码里的隐藏配置错误:你定义的mutation操作名是
uploadImage,但调用时传的op_name是uploadMessageImage,就算解决了序列化问题,后续也会因为操作名不匹配触发请求报错,需要把op_name改成和query定义一致的名称。
修正后的测试调用示例参考:
def test_non_authenticated_request(self, user, client, client_query): # 单独构造测试文件对象 test_file = SimpleUploadedFile( name='test_image.jpg', content=b'fake jpeg binary content', content_type='image/jpeg' ) # 变量字典里移除文件对象,对应位置设为null等待映射 input_data = {"file": None} response = client_query( self.query, op_name="uploadImage", # 和query中定义的mutation名保持一致 variables=input_data, # 单独传入文件、配置映射关系,由client自动生成符合规范的multipart请求 files={"0": test_file}, map={"0": ["variables.file"]} ) content = json.loads(response.content) assert "errors" not in content assert AuthenticationError.__name__ in str(response.content)
内容的提问来源于stack exchange,提问作者Aaron Sta.Clara
相关产品推荐
相关产品推荐

