pytest:下载一次测试文件,并将其用于多个测试

问题描述

我正在使用pytest为正在开发的程序包运行测试用例。测试使用一个小图像文件,我将其保存为github资产。以下代码可以正常工作,但我认为pytest每次运行新测试时都在下载映像,这会花费不必要的时间和资源。我试图弄清楚如何下载一次文件,然后在测试用例之间共享它

这是一些示例代码

# -- in conftest.py --
import sys
import pytest
import os
import shutil
import requests

@pytest.fixture(scope="function")
def small_image(tmpdir):
    url = 'https://github.com/.../sample_image_small.tif'

    r = requests.get(url)

    with open(os.path.join(str(tmpdir),'sample_image_small.tif'),'wb') as f:
        f.write(r.content)

    return os.path.join(str(tmpdir),'sample_image_small.tif')

然后是一些非常简单的测试用例,它们应该能够共享同一张图像。

# -- test_package.py --
import pytest
import os

@pytest.mark.usefixtures('small_image')


def test_ispath(small_image,compression):

    assert os.path.exists(small_image)

def test_isfile(small_image,compression):

    assert os.path.isfile(small_image)

现在,我相信pytest会尝试单独隔离每个测试,这就是导致文件重复下载的原因。我尝试设置@pytest.fixture(scope="module")而不是function,但这产生了奇怪的错误

ScopeMismatch: You tried to access the 'function' scoped fixture 'tmpdir' with a 'module' scoped request object,involved factories

是否有更好的方法来设置测试,以免重复下载文件

解决方法

您可以使用相同的代码,只需自己处理临时文件,而不使用tmpdir固定装置(不能在模块范围的固定装置中使用):

import os
import tempfile
import pytest
import requests

@pytest.fixture(scope="module")
def small_image():
    url = 'https://github.com/.../sample_image_small.tif'

    r = requests.get(url)
    f = tempfile.NamedTemporaryFile(delete=False):
    f.write(f.content)
    yield f.name
    os.remove(f.name)

这将创建文件,返回文件名,并在测试完成后删除文件。

编辑: @hoefling的回答显示了一种更标准的方法,我将把它留作参考。

,

首先,请事先注意:tmpdir / tmpdir_factory是旧的tmp_path / tmp_path_factory灯具对的更好替代,它处理pathlib对象已弃用的py.path中的数据,请参见Temporary directories and files

第二,如果要处理会话作用域(或模块作用域)的文件,tmp*_factory固定装置就是为此而设计的。示例:

@pytest.fixture(scope='session')
def small_image(tmp_path_factory):
    img = tmp_path_factory.getbasetemp() / 'sample_image_small.tif'
    img.write_bytes(b'spam')
    return img

sample_image_small.tif现在将在每次测试运行中写入一次。


当然,正如@MrBean Bremen在他的回答中所建议的,使用tempfile并没有错,这只是做同样的选择,只是使用标准的pytest灯具。