py.test: временная папка для области сеанса

Прибор tmpdir в py.test использует область function и поэтому недоступен в приспособлении с более широкой областью действия, например session. Однако это может быть полезно в некоторых случаях, например, при настройке временного сервера PostgreSQL (который, конечно, не следует воссоздавать для каждого теста).

Есть ли какой-нибудь чистый способ получить временную папку для более широкой области, которая не связана с написанием моего собственного приспособления и доступом к внутренним API-интерфейсам py.test?


person ThiefMaster    schedule 27.08.2014    source источник


Ответы (4)


К сожалению, в настоящее время нет возможности сделать это хорошо. В будущем py.test представит новую область видимости «любая» или что-то подобное, но это будущее.

Прямо сейчас вам придется сделать это вручную. Однако, как вы заметили, вы теряете довольно много приятных функций: символические ссылки в / tmp на последний тест, автоматическую очистку после нескольких запусков тестов, каталоги с разумными именами и т. Д. Если каталог не слишком дорогой, я обычно комбинирую фикстуру с ограниченным объемом функций и сеансом. следующим образом:

@pytest.fixture(scope='session')
def sessiondir(request):
    dir = py.path.local(tempfile.mkdtemp())
    request.addfinalizer(lambda: dir.remove(rec=1))
    # Any extra setup here
    return dir

@pytest.fixture
def dir(sessiondir, tmpdir):
    sessiondir.copy(tmpdir)
    return tmpdir

Это создает временный каталог, который очищается после выполнения теста, однако для каждого теста, который действительно в нем нуждается (путем запроса dir), создается копия, которая сохраняется с семантикой tmpdir.

Если тесты действительно должны передавать состояние через этот каталог, то финализатор dir должен будет скопировать все обратно в каталог сеанса. Однако это не очень хорошая идея, поскольку из-за этого тесты зависят от порядка выполнения, а также могут вызвать проблемы при использовании pytest-xdist.

person flub    schedule 27.08.2014
comment
У нас возникнут какие-либо проблемы, если мы просто скопируем встроенный tmpdir под новое имя подключаемого модуля и охватим его по своему усмотрению? - person ajwood; 28.07.2015
comment
Вероятно, IIRC имя tmpdir зависит от области действия функции, и это может вызвать проблемы. Но не проверял, попробуйте, если хотите! - person flub; 31.07.2015
comment
Начиная с версии 2.8 это уже не так. См. stackoverflow.com/a/38050261/384617. - person David Pärsson; 13.06.2017

Начиная с версии pytest 2.8 и выше, доступен фикстур tmpdir_factory с привязкой к сеансу. См. Приведенный ниже пример из документации.

# contents of conftest.py
import pytest

@pytest.fixture(scope='session')
def image_file(tmpdir_factory):
    img = compute_expensive_image()
    fn = tmpdir_factory.mktemp('data').join('img.png')
    img.save(str(fn))
    return fn

# contents of test_image.py
def test_histogram(image_file):
    img = load_image(image_file)
    # compute and test histogram
person itsafire    schedule 27.06.2016

Я добавляю финализатор, когда хочу удалить все временные папки, созданные в сеансе.

_tmp_factory = None
@pytest.fixture(scope="session")
def tmp_factory(request, tmpdir_factory):
    global _tmp_factory
    if _tmp_factory is None:
        _tmp_factory = tmpdir_factory
        request.addfinalizer(cleanup)
    return _tmp_factory

def cleanup():
    root = _tmp_factory.getbasetemp().strpath
    print "Cleaning all temporary folders from %s" % root
    shutil.rmtree(root)

def test_deleting_temp(tmp_factory):
    root_a = tmp_factory.mktemp('A')
    root_a.join('foo.txt').write('hello world A')

    root_b = tmp_factory.mktemp('B')
    root_b.join('bar.txt').write('hello world B')

    for root, _, files in os.walk(tmp_factory.getbasetemp().strpath):
        for name in files:
            print(os.path.join(root, name))

Результат должен быть таким:

/tmp/pytest-of-agp/pytest-0/.lock
/tmp/pytest-of-agp/pytest-0/A0/foo.txt
/tmp/pytest-of-agp/pytest-0/B0/bar.txt
Cleaning all temporary folders from /tmp/pytest-of-agp/pytest-0
person asterio gonzalez    schedule 22.12.2017

Вот еще один подход. Похоже, pytest не удаляет временные каталоги после запуска тестов. Ниже приведен обычный прибор с функциональной областью видимости.

# conftest.py
TMPDIRS = list()

@pytest.fixture
def tmpdir_session(tmpdir):
    """A tmpdir fixture for the session scope. Persists throughout the session."""
    if not TMPDIRS:
        TMPDIRS.append(tmpdir)
    return TMPDIRS[0]

И чтобы иметь постоянные временные каталоги в модулях вместо всего сеанса pytest:

# conftest.py
TMPDIRS = dict()

@pytest.fixture
def tmpdir_module(request, tmpdir):
    """A tmpdir fixture for the module scope. Persists throughout the  module."""
    return TMPDIRS.setdefault(request.module.__name__, tmpdir)

Изменить: вот еще одно решение, которое не включает глобальные переменные. pytest 1.8.0 представил tmpdir_factory прибор, который мы можем использовать:

@pytest.fixture(scope='module')
def tmpdir_module(request, tmpdir_factory):
    """A tmpdir fixture for the module scope. Persists throughout the module."""
    return tmpdir_factory.mktemp(request.module.__name__)


@pytest.fixture(scope='session')
def tmpdir_session(request, tmpdir_factory):
    """A tmpdir fixture for the session scope. Persists throughout the pytest session."""
    return tmpdir_factory.mktemp(request.session.name)
person Robpol86    schedule 08.08.2015
comment
Почему бы вам не использовать scope='session' и scope='module' вместо того, чтобы возиться с глобальными переменными? - person ThiefMaster; 08.08.2015
comment
Что касается неотъемлемой части, это полуправда: она хранит последние 3 временных каталога, которые упоминаются где-то в документации, то есть это функция. - person ThiefMaster; 08.08.2015
comment
Вы не можете использовать scope = 'session' или 'module', когда используете встроенное приспособление tmpdir. Вы получаете исключение, сообщающее, что вы пытаетесь использовать приспособление с функциональной областью в приспособлении области сеанса / модуля. Следовательно, это обходной путь. - person Robpol86; 08.08.2015
comment
Ах да ... теперь, когда вы упомянули об этом, я помню, что столкнулся с той же проблемой. Я бы, наверное, попробовал запустить исходный tempdir код в настраиваемом приспособлении ... - person ThiefMaster; 09.08.2015