pythonpytestfixtures

Pytest fixture is changing the instance returned by another fixture


I'm very baffled and a little concerned to discover the following behaviour where I have two tests and two fixtures.

import pytest


@pytest.fixture
def new_object():
    return list()


@pytest.fixture
def a_string(new_object):
    # Change this instance of the object
    new_object.append(1)
    return "a string"


def test_1(new_object):
    assert len(new_object) == 0


def test_2(a_string, new_object):
    assert len(new_object) == 0

The first test passes but the second one fails.

    def test_2(a_string, new_object):
>       assert len(new_object) == 0
E       assert 1 == 0
E        +  where 1 = len([1])

tests/test_pytest_list.py:21: AssertionError
================================================ short test summary info =================================================
FAILED tests/test_pytest_list.py::test_2 - assert 1 == 0
============================================== 1 failed, 1 passed in 0.36s ===============================================

I expected fixtures to pass new instances of an object (unless specified otherwise), not the same object that some other fixture has modified.

According to the documentation about the scope of fixtures it says:

the default is to invoke once per test function

Does another fixture not qualify as a function?

UPDATE

Based on the comments I now understand the issue, although I still think it's a dangerous behaviour for a unit-testing tool.

Here's another invalid use of fixtures which a naive person like myself might not realize is wrong:

@pytest.fixture
def new_object():
    """I want to test instances of this class"""
    return list()


@pytest.fixture
def case_1(new_object):
    new_object.append(1)
    return new_object


@pytest.fixture
def case_2(new_object):
    new_object.append(2)
    return new_object


def test_cases(case_1, case_2):
    assert sum(case_1) + sum(case_2) == 3  # fails: assert (3 + 3) == 3

Solution

  • The fixture new_object is invoked for each test as you clearly stated from the documentation.

    The issue lies within your second fixture and the usage of the combination of both in your second test.

    As pytest allows you to use fixtures more than once per test without affecting each other by using cached returns.

    That means as the fixture a_word uses the fixture new_object the second time new_object is encountered in your second test it uses the cached return value from the a_word fixture call instead of giving you another empty list.