Pytest and autouse

Hello, Anyone can you explain me why in pytest fixture using autouse and this fixture return a value doesn’t work?

I create a example very basic.

@pytest.fixture(autouse=True, scope="session")
def get_date():
    now = time.time()
    time.sleep(1)
    return get_date

def test_mytime():
    assert get_date < time.time()

get_date never took the value. If I call the fixture in the parameter method them it is working

def test_mytime(get_date):
    assert get_date < time.time()

Please what is wrong, thanks.

you have to pass a fixture in as an argument to the test function just like you did later, so nothing is wrong

Hi Albert, if I pass the fixture as argument is working, I know but I would need to pass it because I set the fixture as autorute=True so that must to run before I run the test in automatic

Sorry, misunderstood your situation. I never use autouse myself.
But I notice your fixture seems to retune itself, is that intentional? Shouldn’t it return the value of now which you currently don’t do anything with? Perhaps that’s the problem?

You are rigth, I wrote wrong is return now, but It doesn’t work anyway, autouse is not working

Pytest doesn’t modify the globals, so the global variable accessed via get_date points to the function. It you want to use the value of a fixture, it needs to be in the parameter list.

Firstly, the fixture’s return-statement is incorrect. Rather than providing the function’s name, surely it is now that is the pertinent data?

Yes, autouse will make such a fixture available to all tests (within scope), whereas a normal(?) fixture will be passed explicitly as an argument to the test-function.

However, if there is data to be transferred from the fixture into the test, then that should appear in the parameter list.

Accordingly, my understanding/rule-of-thumb (YMMV!) - with plain fixtures, the fixture’s function-name is the argument. With autouse fixtures, the yielded-data is provided as an argument instead.

Extracted from a recent project where we tussled with this very question:

@pytest.fixture
def db_connection():
   db = connect_db()
   return db

def test(db_connection):
   # db_connection available for use

became:

@pytest.fixture(autouse=True)
def db_connection():
   db = connect_db()
   yield db

def test(db):
   # db applied directly

Thus, (with assumptions):

@pytest.fixture(autouse=True, scope="session")
def get_date():
    now = time.time()
    time.sleep(1)
    return now

def test_mytime( now ):
    assert now < time.time()

NB code untested (hah!), and under-proviso that rarely utilise autouse.

1 Like

Really? pytest magically analyzes the yield statements, and binds that variable as a fixture available to other functions? or where does the db for test come from? Do you have any documentation link for that?

Hello Neil, I have just tested your code but doesn’t work, thanks anyway for your reply

@d_n’s reply is mostly on target. There’s just a minor typo, which is…

The fixture is not named “now”, but “get_date”.
So it should read

def test_mytime(get_date):
    assert get_date < time.time()

Autouse is not really relevant to this use case. When you want to get the value produced by a fixture, you must pass it by name in order for pytest to pick up on the fact that a test uses a particular fixture.

Autouse lets you specify that a fixture function should be run even when its value is not requested.

For example, here’s a fixture which disables sleeps but returns nothing:

@pytest.fixture(autouse=True)
def _no_sleep(monkeypatch):
    monkeypatch.setattr("time.sleep", lambda t: None)
    yield

In my experience, patching like this is the main use case for autouse fixtures, since it modifies state and doesn’t always have a useful return value.

I would also recommend naming most fixtures as nouns, rather than verbs or “get_X” getters. You’ll find that it reads more naturally in your consuming code, since the value bound to the fixture name is the return value of the fixture function.


EDIT: Oh, and I forgot to mention. yield and return are both supported by pytest, with slightly different meanings.

1 Like

@sirosen your purpose is the same than me. If you see my main question is why I need to put
get_date in the parameter method when I set the fixture with autouse=True which run in automatic, I hope getting the value in the method with get_date

Pytest works like this:

If you have a fixture and a test which refers to the fixture:

@pytest.fixture
def get_date():
    return time.time()

def test_mytime(get_date):
    assert get_date < time.time()

Pytest will automatically call the fixture function and the function’s return value will be available as get_date within the test function test_mytime.

Now if you remove the parameter get_date from test_mytime, the test will still find get_date, but this time it is not the return value of your fixture function, but the function itself!

Adding autouse=True does not help you at all because even though the fixture is executed before your test runs, its result will still not be available within your test.

1 Like