Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid mutating objects from session scope fixtures in PyTest?

What is the best way to avoid side effects on objects returned from session-scoped fixtures?

My recipe is to wrap a session-scoped fixture with a function-scoped one, which returns a copy of the original object. Is there anything built-in in PyTest for that? Some decorator maybe?

import pytest
from pandas import read_csv

@pytest.fixture(scope='session')
def _input_df():
    """Computationally expensive fixture, so runs once per test session.
    As an example we read in a CSV file 5000 rows, 26 columns into a pandas.DataFrame
    """
    df = read_csv('large-file.csv')

    return df


@pytest.fixture(scope='function')
def input_df(_input_df):
    """"This is a function-scoped fixture, which wraps around the session-scoped one
    to make a copy of its result."""
    return _input_df.copy()


def test_df_1(input_df):
    """Inadvertently, this test mutates the object from the input_df fixture"""
    # adding a new column
    input_df['newcol'] = 0
    # we now have 27 columns
    assert input_df.shape == (5000, 27)


def test_df_2(input_df):
    """But since we are getting a copy or the original this test still passes"""
    assert input_df.shape == (5000, 26)
like image 331
Victor Olex Avatar asked Nov 06 '25 23:11

Victor Olex


1 Answers

You should return the copy function object from input_df fixture without instantiating it:

import pytest
from pandas import read_csv

@pytest.fixture(scope='session')
def _input_df():
    """Computationally expensive fixture, so runs once per test session.
    As an example we read in a CSV file 5000 rows, 26 columns into a pandas.DataFrame
    """
    df = read_csv('large-file.csv')

    return df


@pytest.fixture(scope='function')
def input_df(_input_df):
    """"This is a function-scoped fixture, which wraps around the session-scoped one
    to make a copy of its result."""
    return _input_df.copy


def test_df_1(input_df):
    """Inadvertently, this test mutates the object from the input_df fixture"""
    # adding a new column
    input_df()['newcol'] = 0
    # we now have 27 columns
    assert input_df().shape == (5000, 27)


def test_df_2(input_df):
    """But since we are getting a copy or the original this test still passes"""
    assert input_df().shape == (5000, 26)

This code sample is working in my machine. test_df_2 test fails in this case.

like image 190
SilentGuy Avatar answered Nov 08 '25 15:11

SilentGuy