问题 Flask单元测试:获取响应的重定向位置


我有一个基于Flask的webapp,当以某种方式发布到他们的父文档时偶尔用新的随机密钥创建新文档。新密钥进入父级的数据结构,更新的父级临时存储在会话中,并且在成功保存子文档后,存储的父级被拉出会话并存储在一起,以便将两者链接在一起。这是针对某些类型的关系完成的,其中一个人希望在键之间具有固有顺序,因此键被存储为父母的列表。

现在,当我想使用Werkzeug提供的单元测试客户端进行单元测试时,问题出现了。做一个

 ret = self.test_client.post(
    request_path,
    data=data,
    follow_redirects=True
)

在测试用例对象中将成功使用新密钥重定向到子文档 - 但我不知道在单元测试中检索此新密钥的位置。我无法在返回值的顶部找到一个属性,该属性将指示它被重定向到的位置。 dir(ret) 给我

 ['__call__', '__class__', '__delattr__', '__dict__', '__doc__', '__enter__', '__exit__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_ensure_sequence', '_get_mimetype_params', '_on_close', '_status', '_status_code', 'accept_ranges', 'add_etag', 'age', 'allow', 'autocorrect_location_header', 'automatically_set_content_length', 'cache_control', 'calculate_content_length', 'call_on_close', 'charset', 'close', 'content_encoding', 'content_language', 'content_length', 'content_location', 'content_md5', 'content_range', 'content_type', 'data', 'date', 'default_mimetype', 'default_status', 'delete_cookie', 'direct_passthrough', 'expires', 'force_type', 'freeze', 'from_app', 'get_app_iter', 'get_data', 'get_etag', 'get_wsgi_headers', 'get_wsgi_response', 'headers', 'implicit_sequence_conversion', 'is_sequence', 'is_streamed', 'iter_encoded', 'last_modified', 'location', 'make_conditional', 'make_sequence', 'mimetype', 'mimetype_params', 'response', 'retry_after', 'set_cookie', 'set_data', 'set_etag', 'status', 'status_code', 'stream', 'vary', 'www_authenticate']

那些, headers 和 location 看起来很有前途 - 但是没有设置位置,标题也不包含它。

如何从响应中获取重定向的位置?我是否真的必须从响应机构解析孩子的密钥?有没有更好的方法?


3427
2018-03-21 18:21


起源

如果情况变得更糟,则无法自动跟踪重定向并让您的测试手动完成。我同意应该有更好的方法。 - bwbrowning
您不能在重定向页面的响应中查找某些内容以确认重定向发生了吗?如果这不是一个选项,那么禁用重定向,如@bwbrowning所说。 - Miguel


答案:


根据您自己的答案,根据您个人对单元测试风格的偏好,可以随意忽略,您可能更喜欢以下建议,以简化和提高单元测试的清晰度和可读性:

response = self.test_client.post(
    request_path,
    data=data,
    follow_redirects=False
)
expectedPath = '/'
self.assertEqual(response.status_code, 302)
self.assertEqual(urlparse(response.location).path, expectedPath)

13
2018-04-28 19:09



很好,出于某种原因我还没有意识到urlparse。我认为这是因为它比我的版本更清晰。 - Michel Müller
谨防: urlparse().path 不仅修剪模式和主机,还修剪查询字符串。如果要针对带参数的路径进行测试,请不要使用它。 - krassowski


@bwbrowning提供了正确的提示 - 做一个帖子 follow_redirects=False 返回值有 location 属性集 - 这是包含参数的完整请求路径。

编辑:提示 - 做这件事时有轻微的问题 test_client.get(..)  - path参数需要是相对路径,而 ret.location 返回完整路径。所以,我做的是

child_path_with_parameters = rv.location.split('http://localhost')[1]
child_path = child_path_with_parameters.split('?')[0]
ret = self.test_client.get(child_path_with_parameters)

(稍后使用child_path以便发布给孩子)


1
2018-03-22 16:06