Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

several names for fixture #3655

Closed
ggens opened this issue Jul 3, 2018 · 23 comments
Closed

several names for fixture #3655

ggens opened this issue Jul 3, 2018 · 23 comments
Labels
status: needs information reporter needs to provide more information; can be closed after 2 or more weeks of inactivity type: enhancement new feature or API change, should be merged into features branch type: proposal proposal for a new feature, often to gather opinions or design the API around the new feature

Comments

@ggens
Copy link

ggens commented Jul 3, 2018

@#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)

@pytestbot
Copy link
Contributor

GitMate.io thinks possibly related issues are #2934 (capsysbinary fixture), #3377 (Rescope a fixture), #538 (Fixture scope documentation), #794 (fixture named "request" fails), and #484 (Order of class fixtures).

@pytestbot pytestbot added platform: mac mac platform-specific problem type: enhancement new feature or API change, should be merged into features branch labels Jul 3, 2018
@nicoddemus nicoddemus added type: proposal proposal for a new feature, often to gather opinions or design the API around the new feature and removed platform: mac mac platform-specific problem labels Jul 4, 2018
@nicoddemus
Copy link
Member

nicoddemus commented Jul 4, 2018

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.

@RonnyPfannschmidt
Copy link
Member

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 RonnyPfannschmidt added the status: needs information reporter needs to provide more information; can be closed after 2 or more weeks of inactivity label Jul 4, 2018
@ggens
Copy link
Author

ggens commented Jul 4, 2018

@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))

@RonnyPfannschmidt
Copy link
Member

@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

@ggens
Copy link
Author

ggens commented Jul 4, 2018

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 ?

@RonnyPfannschmidt
Copy link
Member

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

@nicoddemus
Copy link
Member

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

@ggens
Copy link
Author

ggens commented Jul 5, 2018

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))

@RonnyPfannschmidt
Copy link
Member

@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

@nicoddemus
Copy link
Member

@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.

@ggens
Copy link
Author

ggens commented Jul 6, 2018

@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.

@RonnyPfannschmidt
Copy link
Member

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

@nicoddemus
Copy link
Member

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)

@RonnyPfannschmidt
Copy link
Member

If that was it they wouldnt add value, -1

@ggens
Copy link
Author

ggens commented Jul 9, 2018

@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)

@nicoddemus
Copy link
Member

@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).

@RonnyPfannschmidt
Copy link
Member

@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 ^^

@nicoddemus
Copy link
Member

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

@sp-ricard-valverde
Copy link

sp-ricard-valverde commented Jul 28, 2020

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

    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:

    @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.

@laike9m
Copy link

laike9m commented Dec 5, 2020

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

@The-Compiler
Copy link
Member

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

yield from other_fixture probably would though, no?

@laike9m
Copy link

laike9m commented Dec 6, 2020

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: needs information reporter needs to provide more information; can be closed after 2 or more weeks of inactivity type: enhancement new feature or API change, should be merged into features branch type: proposal proposal for a new feature, often to gather opinions or design the API around the new feature
Projects
None yet
Development

No branches or pull requests

7 participants