How to call a fixture from another fixture in Pytest?
Asked Answered
B

4

20

I have a fixture that is returning an object of certain type and I have another fixture defined in another file that basically uses the object to do other things. But I am not able to return the object from my first fixture.

file-1

def fixture_1(s, **kwargs):
    def hook(s, **kwargs):
        p_b = s.get()
        p = p_b.build()
        yield p
    return hook

file-2 conftest.py

@pytest.fixture(scope='module')
def fixture_1(s, **kwargs):
    def hook(s, **kwargs):
        #Default implementation is no-op.
        pass
    return hook

@pytest.fixture(scope='module')
def fixture_2(s,b_p):
    some_p = fixture_1(s)
    current_status = s.start(some_p)

    print(current_status)
    yield current_status

I want to basically retrieve object p returned in file-1 fixture_1 and use it in file-2 fixture_2 fixture.

Bernstein answered 31/5, 2019 at 23:9 Comment(1)
Some notes: 1) file-1 fixture_1 is not decorated with @pytest.fixture 2) it has the same name as file-2 fixture_1 3) it is returning a generator function, do you really want it?Shifra
W
10

Easy example included

Py.test supports fixture invoking other fixtures straight out of the box

Have the fixtures in conftest.py or in test file:

conftest.py:

@pytest.fixture(scope="session")
def fixture_A():
    some_obj = create_obj()
    return some_obj  # or use yield some_obj in case you want to destruct

@pytest.fixture(scope="session")
def fixture_B(fixture_A):
   this_is_some_obj = fixture_A
   # do something
   another = {}
   return this_is_some_obj, another

test_example.py:

@pytest.fixture(scope="session")
def fixture_C(fixture_B):
   return fixtureB

def test_finally_the_test(fixture_C):
    some_obj, another = fixture_C
 

Important to mention that the fixtures above will be invoked once (even if multiple tests uses these fixtures) - this is due to the "session "scope of each fixture it is just as if these fixtures were singletons (if compared to OOP)

Another note pytest knows the order to run the fixtures (it checks the dependencies - so nothing special to do here)

Wendalyn answered 6/7, 2022 at 17:53 Comment(1)
Did you tested that ? I got an issue with fixture_A not being foundCleo
F
6

It seems that you are using pytest fixtures wrong (looking at your arguments names).

I'd recommend you to go through https://docs.pytest.org/en/latest/fixture.html

There are two solutions for your problem:

###
# file_1
def not_really_a_fixture(s, **kwargs): # just some hook generator
    def hook(s, **kwargs):
        p_b = s.get()
        p = p_b.build()
        yield p
    return hook


###
# conftest.py
from file_1 import not_really_a_fixture

@pytest.fixture(scope='module')
def fixture_2(s,b_p): # s and b_p should be names of fixtures that need to run before this
    some_p = not_really_a_fixture(s)
    current_status = s.start(some_p)

    print(current_status)
    yield current_status

And:

###
# file_1
@pytest.fixture(scope='module')
def fixture_1(s): # s is name of another fixture
    # there is no point in having **kwargs as arg in pytest fixture
    def hook(s, **kwargs):
        #Default implementation is no-op.
        pass
    return hook

###
# conftest.py
from file_1 import fixture_1

@pytest.fixture(scope='module')
def fixture_2(s,b_p,fixture_1): # s and b_p should be names of fixtures that need to run before this
    # in fixture_1 is value returned by fixture_1, that means your hook func
    current_status = s.start(fixture_1)

    print(current_status)
    yield current_status
Fruin answered 16/10, 2019 at 10:52 Comment(0)
V
1

For example, test() can call fixture_2() which can call fixture_1() as shown below:

import pytest

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

@pytest.fixture
def fixture_2(fixture_1):
    return fixture_1

def test(fixture_2):
    print(fixture_2)
    assert True

Output:

$ pytest -q -rP
.                                [100%]
=============== PASSES ================ 
________________ test _________________ 
-------- Captured stdout call --------- 
fixture_1
1 passed in 0.10s
Vaccination answered 8/9, 2023 at 21:46 Comment(0)
C
0

I encountered this error because my scope was too high.

TLDR: Verify that your scopes are correct, and if needed, set the child to a lower scope – or create a new fixture from the parent in the child scope. scope="module" was the culprit for me. I set to a lower scope - "function" and it worked.

Story time:

Here's what I was trying to do (oversimplified):

1. Test a class which uses pytest's temporary directory:

import dataclasses


@dataclasses.dataclass
class Storage:
    directory: str


def test_my_class(tmpdir):
    """Ensure Storage object can be instantiated"""
    s = Storage(directory=tmpdir)
    assert s.directory == str(tmpdir)

💡 However, I was writing dozens of tests, and I did not want to instantiate dozens of storage class objects.

2. Instead, I created this fixture:

@pytest.fixture()
def my_storage(tmpdir) -> Storage:
    return Storage(directory=tmpdir)

def test_my_class(my_storage, tmpdir):
    """Ensure Storage object can be instantiated"""
    assert my_storage.directory == tmpdir

💡 But I wanted the scope to be on the module level.

3. Setting scope to module level causes error:

@pytest.fixture(scope="module")
def my_storage(tmpdir) -> Storage:
    return Storage(directory=tmpdir)

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

4. Easy solution = Set scope to function (default):

@pytest.fixture(scope="function")
def my_storage(tmpdir) -> Storage:
    return Storage(directory=tmpdir)

This works. But what if I needed the fixture on the module level?

5. Hard solution = Create a temporary directory as a fixture, in the designated scope:

@pytest.fixture(scope="module")
def my_tmpdir(tmpdir_factory):
    return tmpdir_factory.mktemp("data")

@pytest.fixture(scope="module")
def my_storage(my_tmpdir) -> Storage:
    return Storage(directory=my_tmpdir)

def test_my_class(my_storage, my_tmpdir):
    """Ensure Storage object can be instantiated"""
    assert my_storage.directory == my_tmpdir
Counterpoison answered 28/10, 2023 at 21:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.