Pytest: several names for fixture

Created on 3 Jul 2018  路  23Comments  路  Source: pytest-dev/pytest

@#hello everyone,

I've suggestion, Is it possible to add list string management for name parameter ?

@pytest.fixture(name=[ 'name_1', 'name_2])
def fixture_factory:
    ...

of course request.fixturename == 'name_1' or 'name_2' (depends fixture call by test)

needs information enhancement proposal

Most helpful comment

Thanks @ggens,

FWIW, this is how I imagine class fixtures would work:

@pytest.fixture(scope='module')
class MyFixture:

    def __init__(self, tmpdir):
        ...

Would be the equivalent of:

@pytest.fixture(scope='module')
def my_fixture(tmpdir):

    class MyFixture:

        def __init__(self, tmpdir):
            ...

    return MyFixture(tmpdir)

All 23 comments

GitMate.io thinks possibly related issues are https://github.com/pytest-dev/pytest/issues/2934 (capsysbinary fixture), https://github.com/pytest-dev/pytest/issues/3377 (Rescope a fixture), https://github.com/pytest-dev/pytest/issues/538 (Fixture scope documentation), https://github.com/pytest-dev/pytest/issues/794 (fixture named "request" fails), and https://github.com/pytest-dev/pytest/issues/484 (Order of class fixtures).

Hi @ggens, thanks for writing.

The current workaround for that is:

@pytest.fixture(name='name_1')
def fixture_factory_1():
    ...

@pytest.fixture(name='name_2')
def fixture_factory_2(name_1):
    return name_1

But I'm curious, what use cases do you have in mind?

One that I can think of is deprecating one fixture name over the another (for example), but even in that case I would still want to raise a deprecation warning on the deprecated fixture.

the main issue that op didnt explain what he/she wants to actually do

without the use-case we have no idea whats actually meant

@RonnyPfannschmidt , I would like collect request.fixturename to pass into class factory.
```
class abstract_factory(object):
def __new__(cls, fixturename):
if fixturename == "fixture_1":
return str(cls)
if fixturename == "fixture_2":
return bool(cls)

@pytest.fixture(name=[ 'fixture_1', 'fixture_2'])
def fixture_factory(request):
return abstract_factory(request.fixturename)

def test_fixture_factory(fixture_1,fixture_2):
assert not isinstance(fixture_1,type(fixture_2))
```

@ggens based on your examples its a much better pattern to just declare a factory fixture and use it from other fixtures like pytests own tmpdir factory for example

I looked tmpdir factory pattern by my requirement is different of this usage by these points:

  • scope function for fixture_factory
  • many call of this fixture for one test
    ... I don't see how tmpdir factory pattern answer of these points. My requirement is not maybe the good "pytest-way" (or pythonic) but ... one fixture must be only specific behavior ?

one thing is certain, you are not describing your actual use-case as long as you dont, there is nothing we can do to help you, im not going to participate in this as longer as its just a X-Y problem

@ggens perhaps some example code demonstrating what you want to do (even if it does not work) would help?

my actual case seems :

class abstract_factory(object):
    def __new__(cls, fixturename):
        if fixturename == "fixture_1":
            return str(cls)
        if fixturename == "fixture_2":
            return bool(cls)

@pytest.fixture
def fixture_1(request):
    return abstract_factory("fixture_1")

@pytest.fixture
def fixture_2(request):
    return abstract_factory("fixture_2")

def test_fixture_factory(fixture_1,fixture_2):
   assert not isinstance(fixture_1,type(fixture_2))
 ```
... and i would like just this 

class abstract_factory(object):
def __new__(cls, fixturename):
if fixturename == "fixture_1":
return str(cls)
if fixturename == "fixture_2":
return bool(cls)

@pytest.fixture(name=[ 'fixture_1', 'fixture_2'])
def fixture_factory(request):
return abstract_factory(request.fixturename)

def test_fixture_factory(fixture_1,fixture_2):
assert not isinstance(fixture_1,type(fixture_2))
```

@ggens that is a specific example if a implementation that is trimmed down so much that tis no longer a use-case, but rather a disconnected code example of no value

@ggens thanks for posting, but as @RonnyPfannschmidt said, your examples doesn't convey why the 1st example is much superior to the second, besides being just a little shorter (17 vs 13 lines).

Can you post real world scenarios where this would be useful? I can see it being used whenever someone has a factory and a fixed set of instantiations, but this seems like a rare use case.

@RonnyPfannschmidt @nicoddemus afterthought , I'm agree with you. It's too specific case which does not bring anything.
Well, I think new design for my case but should need class fixture

May you talk me about this ? is it planned ? just an idea? people work on this feature ? ( i'm really interested to participate)
thanks a lot again.

currently nobody is working on it, its not clear how to enable it yet

Thanks @ggens,

FWIW, this is how I imagine class fixtures would work:

@pytest.fixture(scope='module')
class MyFixture:

    def __init__(self, tmpdir):
        ...

Would be the equivalent of:

@pytest.fixture(scope='module')
def my_fixture(tmpdir):

    class MyFixture:

        def __init__(self, tmpdir):
            ...

    return MyFixture(tmpdir)

If that was it they wouldnt add value, -1

@RonnyPfannschmidt I'm not agree with you, example of @nicoddemus show an add value with the first case: Avoiding to encapsulate class in function for potentially an other usage (and it's join my issue in my current work)

@RonnyPfannschmidt I don't see much value either, but I think if this can be implemented cleanly and non-intrusively I wouldn't mind adding it (unless we can already foresee problems with the approach).

@nicoddemus i am very strictly opposed to burning class level fixtures as just a new way to spell functions when we could get real and actual interaction out of it ^^

Oh that's a good point. I agree then that we should wait until a more compelling use case appears. 馃憤

Sorry to reopen this, but I might have actually found a use case for the multiple naming of a single fixture:

````python
def path_glob(...):
# some function to be tested

@pytest.fixture
def match_files(request):
    files = request.param
    return {f.replace('/', os.sep) for f in files}

@pytest.fixture
def unmatch_files(request):
    files = request.param
    return {f.replace('/', os.sep) for f in files}

@pytest.mark.parametrize('mkfiles, match_files, path, expr', [
    ({'file_a.txt', 'file_b.txt'}, {'file_a.txt', 'file_b.txt'}, '.', '*.txt'),
    ({'file_a.txt', 'dir/file_b.txt'}, {'file_a.txt'}, '.', '*.txt'),
    ({'file_a.txt', 'dir/file_b.txt'}, {'file_a.txt', 'dir/file_b.txt'}, '.', '**/*.txt'),
    ({'file_a.txt', 'dir/file_b.txt'}, {'dir/file_b.txt'}, 'dir', '*.txt'),
    ({'file_A.txt'}, {'file_A.txt'}, '.', '*.txt'),
    ({'.file_a.txt'}, {'.file_a.txt'}, '.', '*.txt')
], indirect=['mkfiles', 'match_files'])
def test_path_glob_ok(self, chdir_tmp, mkfiles, match_files, path, expr):
    found_files = path_glob(Path(path), expr)
    found_files_set = {str(f) for f in found_files}
    assert found_files_set == match_files

@pytest.mark.parametrize('mkfiles, unmatch_files, path, expr', [
    ({'file_a.txt', 'file_b.png'}, {'file_b.png'}, '.', '*.txt'),
    ({'file_a.txt', 'file_b.txt', 'dir/file_c.txt'}, {'dir/file_c.txt'}, '.', '*.txt')
], indirect=['mkfiles', 'unmatch_files'])
def test_path_glob_ok_unmatch(self, chdir_tmp, mkfiles, unmatch_files, path, expr):
    found_files = path_glob(Path(path), expr)
    found_files_set = {str(f) for f in found_files}
    assert not (found_files_set & unmatch_files)

````

In this case match_files and unmatch_files could easily be the same fixture with different appropriate names, but the alternate solution:

````python
@pytest.fixture
def match_files(request):
files = request.param
return {f.replace('/', os.sep) for f in files}

@pytest.fixture
def unmatch_files(match_files):
    return match_files

````

Won't work as the request context is lost and cannot be passed from unmatch_files to match_files.

Would love to see this. Return from another fixture doesn't work if using yield.

Return from another fixture doesn't work if using yield.

yield from other_fixture probably would though, no?

Return from another fixture doesn't work if using yield.

yield from other_fixture probably would though, no?

Which gives the error "Fixtures are not meant to be called directly"

I have not found a good way to reuse a fixture without defining a non-fixture function and call it in both fixtures.

Was this page helpful?
0 / 5 - 0 ratings