question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

request.getfixturevalue crashes in certain cases.

See original GitHub issue

conftest.py

import pytest

@pytest.fixture
def fixture_a(request):
  yield 'a'

@pytest.fixture(scope="session")
def accumulator():
  # This fixture that accumulates info from each test case (via the record_data fixture)
  # and then writes it to a database once we're all done
  accumulator = []
  yield accumulator
  # IRL we'd upload this to a database or something.  In this minimum repro we'll just print it.
  print(f'Printed by accumulator.  All tests recorded {accumulator}')

@pytest.fixture
def record_data(accumulator, request):

  test_data = {}
  yield test_data
  print(f"Printed by record_data fixture - test provided data: {test_data}")

  # If fixture_a was used by the test case, record its value too.
  if 'fixture_a' in request.fixturenames:
    test_data['fixture_a'] = request.getfixturevalue('fixture_a')

  accumulator.append(test_data)

I have a fixture called record_data that lets a test case store some extra info and then send it off to a database once all the tests are done running. If I run the following test case:

def test_01(record_data):
  record_data['foo'] = 'blah'
  assert True

I get the following output:

Printed by record_data fixture - test provided data: {'foo': 'blah'}
Printed by accumulator.  All tests recorded [{'foo': 'blah'}]

If I add a second test case that uses fixture_a then the record_data fixture will pick that up and record some information about fixture_a too:

def test_02(fixture_a, record_data):
  record_data['foo'] = 'blah from test_02'
  assert fixture_a == 'a'

Now I get this output:

Printed by record_data fixture - test provided data: {'foo': 'blah'}
Printed by record_data fixture - test provided data: {'foo': 'blah from test_02'}
Printed by accumulator.  All tests recorded [{'foo': 'blah'}, {'foo': 'blah from test_02', 'fixture_a': 'a'}]

By using request.fixturenames and request.getfixturevalue I can snoop the value of fixture_a without explicitly adding it to the arg list of record_data and thus not forcing tests to use fixture_a. I thought this was pretty smart and clever. Unforunately, this working appears to depend on the order that the fixtures are listed in the arg list. If I do

def test_03(record_data, fixture_a):  # The only change is the order of the fixtures
  record_data['foo'] = 'test_03 is where the bug happens'
  assert fixture_a == 'a'

Then things go wrong and I get the following error message:

    @pytest.fixture
    def record_data(accumulator, request):
     
      test_data = {}
      yield test_data
      print(f'Printed by record_data fixture - test provided data: {test_data}')
     
      # If fixture_a was used by the test case, record its value too.
      if 'fixture_a' in request.fixturenames:
>       test_data['fixture_a'] = request.getfixturevalue('fixture_a')
E       AssertionError

I dove in with my trusty debugger. The AssertionError I’m getting comes from request.getfixturevalue because cached_result got set to None here when the fixture was finalized.

Here’s the version of everything I’m using:

(pytest_venv) petebman@petebman:~/pytest_fixture_bug$ python3 -m pip freeze
attrs==21.4.0
iniconfig==1.1.1
packaging==21.3
pluggy==1.0.0
py==1.11.0
pyparsing==3.0.9
pytest==7.1.2
tomli==2.0.1

I originally ran into this issue in pytest version 6.2.3 but I did the minimum viable repro on the latest version to make sure I wasn’t complaining about something that had already been fixed.

I wonder if there’s a way to keep track of which fixtures have been finalized and which ones haven’t yet other than setting cached_value to None? Alternatively is there a different way I should try to do what I’m trying to do here? It seems reasonable that if a fixture’s name is in request.fixturenames then you should be able to get a value via request.getfixturevalue.

Issue Analytics

  • State:closed
  • Created a year ago
  • Comments:6 (6 by maintainers)

github_iconTop GitHub Comments

1reaction
RonnyPfannschmidtcommented, May 21, 2022

imho its fair to fail, you request a fixture in teardown

we should actually let it fail in the teardown phase as its simply wrong to call that api there

put your getfixturevalue before the yield

0reactions
RonnyPfannschmidtcommented, May 21, 2022

@petebman certainly 👍

Read more comments on GitHub >

github_iconTop Results From Across the Web

Investigating memory access crashes
Identify crashes that arise from memory access issues, and investigate the cause of the crash.
Read more >
Changelog — pytest documentation
getfixturevalue () (where request is a FixtureRequest fixture) when a fixture with the given name cannot be returned. issue #6433: If an error...
Read more >
Troubleshoot Chrome crashes
Crashing issues with Chrome or a ChromeOS device can be caused a number of things. Here are some potential issues you might encounter:...
Read more >
How to Fix Crashing Apps on an iPhone or Android?
Here, we have curated some of them for you, explaining some of the easiest and most common hacks to fix an app crash...
Read more >
How to Fix Instagram Crashing
There are a few common and easy methods used to fix a crashing Instagram app on ... In these cases, Instagram simply asked...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found