29

What is the best way to define an object in a fixture with session scope and autouse=True, so it will be available to all tests?

@pytest.fixture(scope='session', autouse=True)
def setup_func(request):
    obj = SomeObj()

Next thing, I want some magic that previously created obj will appear in each test context without the need of each test to define the setup_func fixture.

def test_one():
   obj.do_something_fancy()
alnet
  • 1,093
  • 1
  • 12
  • 24

3 Answers3

26

My recommendation would to add the fixture to conftest.py and make sure to return the object you want to produce from the fixture.

As noted, this makes "autouse" kind of useless.

In the root directory for your tests, add the fixture to a file named conftest.py:

@pytest.fixture(scope='session', autouse=True)
def someobj(request):
    return SomeObj()

Any test file beneath the root file will have access to this fixture (for example test_foo.py):

def test_foo(someobj):
    assert isinstance(someobj, SomeObj)

Another approach, would be to use a global variable defined in the same test or imported from a module.

For example in conftest.py:

someobj = None
@pytest.fixture(scope='session', autouse=True)
def prep_someobj(request):
    someobj = SomeObj()

Then in your test:

from . import conftest

def test_foo():
    assert isinstance(conftest.someobj, SomeObj)

In my opinion this is less readable and more cumbersome than the first method.

Sebastian
  • 2,678
  • 25
  • 24
  • 1
    Is there a way to make someobj be implicitly available to the tests? I don't want the test to "declare" it, as there is no point of autouse in that context. – alnet Nov 12 '15 at 07:12
  • If you can expand (in your question) a little more on how you imagine your tests making use of the SomeObj instance I can probably help with a clearer answer – Sebastian Nov 12 '15 at 07:44
  • Expanded on the original question. Is SomeObj just a regular class or is it some sort of ORM-managed model or some other sort of resource? If so, there could be another suggestion to include – Sebastian Nov 12 '15 at 08:05
  • A maybe better variation of the second approach would be to define a Class and use a custom method with autouse = True (almost like a setup_method function) and have this method get the object you want and store in the instance: https://stackoverflow.com/questions/37436871/py-test-session-level-fixtures-in-setup-method – SuperGeo Oct 16 '17 at 08:38
  • Hey, can we also return a string using the first method? – Sakshi Singla Nov 09 '22 at 06:50
9

A more general pattern for this is to return locals() at the end of your conftest and you'll be able to easily reference anything created in the fixture.

conftest.py

@pytest.fixture(scope='session')
def setup_func(request):
    obj1 = SomeObj()
    obj2 = SomeObj()

    return locals()

test_stuff.py

def test_one(setup_func):
   setup_func['obj1'].do_something_fancy()

def test_two(setup_func):
   setup_func['obj2'].do_something_fancy()
Everett Toews
  • 10,337
  • 10
  • 44
  • 45
5

Another possibility is to wrap your tests in a class and use class variables to only define the object instance once. This assumes you are able to wrap all tests in a single class and so this answer may address a less general, but similar use case. For example,

class SomeObj():
    """This object definition may exist in another module and be imported."""
    def __init__(self):
        self.x = 5

    def do_something_fancy(self, y):
        return self.x * y


class TestX():
    # Object instance to share across tests
    someobj = SomeObj()

    def test_x(self):
        assert TestX.someobj.x == 5

    def test_fancy(self):
        fancy_factor = 10
        result = TestX.someobj.do_something_fancy(fancy_factor)
        assert result == 50
wingr
  • 2,460
  • 24
  • 12