【问题标题】:Mocking download of a file using Python requests and responses使用 Python 请求和响应模拟文件下载
【发布时间】:2016-09-13 20:08:24
【问题描述】:

我有一些 python 代码可以使用requests 成功从 URL 下载图像,并将其保存到/tmp/。我想测试它应该做的事情。我正在使用responses 来测试获取 JSON 文件,但我不确定如何模拟获取文件的行为。

我认为这类似于模拟标准响应,如下所示,但我想我对如何将 body 设置为文件一无所知...

@responses.activate
def test_download():
    responses.add(responses.GET, 'http://example.org/images/my_image.jpg',
              body='', status=200,
              content_type='image/jpeg')
    #...

更新:根据 Ashafix 的评论,我正在尝试这个(python 3):

from io import BytesIO

@responses.activate
def test_download():
    with open('tests/images/tester.jpg', 'rb') as img1:
        imgIO = BytesIO(img1.read())

    responses.add(responses.GET, 'http://example.org/images/my_image.jpg',
              body=imgIO, status=200,
              content_type='image/jpeg')
    imgIO.seek(0)
    #...

但是,随后,当我正在测试的代码尝试执行我得到的请求时:

a bytes-like object is required, not '_io.BytesIO'

感觉几乎是对的,但我很难过。

更新 2: 尝试遵循 Steve Jessop 的建议:

@responses.activate
def test_download():
    with open('tests/images/tester.jpg', 'rb') as img1:
        responses.add(responses.GET, 'http://example.org/images/my_image.jpg',
                  body=img1.read(), status=200,
                  content_type='image/jpeg')
        #...

但是这次被测试的代码提出了这个问题:

I/O operation on closed file.

图片当然应该在with 块内打开?

更新 3: 我正在测试的代码是这样的:

r = requests.get(url, stream=True)
if r.status_code == 200:
     with open('/tmp/temp.jpg', 'wb') as f:
        r.raw.decode_content = True
        shutil.copyfileobj(r.raw, f)

似乎最后的shutil 行正在生成“对已关闭文件的I/O 操作”。错误。我不太了解这一点 - 文件的流式传输 - 知道如何最好地模拟这种行为,测试下载的文件保存到 /tmp/

【问题讨论】:

  • body = open(filename, 'rb').read() 不起作用吗?在实践中,与对open 的任何调用一样,您可能希望将其用作上下文管理器。
  • 谢谢...我已经慢慢提出了这两个建议,但还没有完成!
  • 你有没有试过不使用 with 块,然后简单地使用img1 = open(...),然后使用img1.close()
  • 感谢 Sepehr。我仍然收到“对已关闭文件的 I/O 操作”,即使我只是在调用我正在测试的代码后才关闭它。诡异的!可能与在请求中使用stream 有关吗?我在做r = requests.get(url, stream=True)

标签: python unit-testing python-requests


【解决方案1】:

您可能需要将stream=True 传递给responses.add 调用。比如:

@responses.activate
def test_download():
    with open("tests/images/tester.jpg", "rb") as img1:
        responses.add(
            responses.GET,
            "http://example.org/images/my_image.jpg",
            body=img1.read(),
            status=200,
            content_type="image/jpeg",
            stream=True,
        )

【讨论】:

    【解决方案2】:

    首先,总结一下我现在过长的问题......我正在测试一些代码,类似于:

    def download_file(url):
        r = requests.get(url, stream=True)
        if r.status_code == 200:
             filename = os.path.basename(url)
             with open('/tmp/%s' % filename, 'wb') as f:
                r.raw.decode_content = True
                shutil.copyfileobj(r.raw, f)
             return filename
    

    它会下载一张图片并将其流式传输,然后将其保存到/tmp/。我想模拟请求,以便测试其他内容。

    @responses.activate
    def test_downloads_file(self):
        url = 'http://example.org/test.jpg'
        with open('tests/images/tester.jpg', 'rb') as img:
            responses.add(responses.GET, url,
                            body=img.read(), status=200,
                            content_type='image/jpg',
                            adding_headers={'Transfer-Encoding': 'chunked'})
            filename = download_file(url)
            # assert things here.
    

    一旦我找到了使用 open() 的方法,我仍然得到“对关闭文件的 I/O 操作”。来自shutil.copyfileobj()。阻止这种情况的是添加 Transfer-Encoding 标头,当我发出真正的请求时,它会出现在标头中。

    非常欢迎对其他更好的解决方案提出任何建议!

    【讨论】:

    • 这工作了一段时间,现在不再工作了;我仍然收到I/O operation on closed file.
    • 看起来requests v2.12.0 发生了一些变化,导致了这种情况。安装 v2.11.1 时测试工作正常,但不安装 v2.12.0-v2.13.0。
    猜你喜欢
    • 2017-04-15
    • 1970-01-01
    • 2021-08-06
    • 1970-01-01
    • 1970-01-01
    • 2013-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多