cpage
cpage

Reputation: 39

Parametrize pytest for list and pandas dataframe fixture

I am creating tests inpytest for a custom sub-class of a pandas DataFrame. I would like to test that my class constructor is working for both a list and a pandas DataFrame. How can I parameterize my test so test_constructor can accept both samp_list and samp_df rather than having duplicate test_constructor_with_df and test_constructor_with_list test functions?

@pytest.fixture(scope='module')
def sample():
    samp_list = [{'timestamp': '2020-01-01', 'group': 'a', 'dollar_gains': 100},
    {'timestamp': '2020-01-01', 'group': 'b', 'dollar_gains': 100},
    {'timestamp': '2020-01-01', 'group': 'c', 'dollar_gains': 110},
    {'timestamp': '2020-01-01', 'group': 'a', 'dollar_gains': 110},
    {'timestamp': '2020-01-01', 'group': 'b', 'dollar_gains': 90},
    {'timestamp': '2020-01-01', 'group': 'd', 'dollar_gains': 100}]

    samp_df = pd.DataFrame(samp_list)

    return samp_list, samp_df

def test_constructor(sample):
    print('hi')
    hist_dg = HistDollarGains(sample, 'group', 'timestamp')
    assert hist_dg.group == 'group'
    assert hist_dg.timestamp_col == 'timestamp'

Upvotes: 3

Views: 3638

Answers (1)

Steve_datasci
Steve_datasci

Reputation: 86

This is probably too late to help you, but I ran into a similar question today. Seems like there's an option to pass parameters to the fixture, and that it will run for each value passed.

Martin Winkel did a good article on it here: https://levelup.gitconnected.com/advanced-pytest-techniques-i-learned-while-contributing-to-pandas-7ba1465b65eb

For your case, I think you'd do something like:

import pandas as pd
import pytest

opt_dict = {'samp_list': [{'timestamp': '2020-01-01', 'group': 'a', 'dollar_gains': 100},
                {'timestamp': '2020-01-01', 'group': 'b', 'dollar_gains': 100},
                {'timestamp': '2020-01-01', 'group': 'c', 'dollar_gains': 110},
                {'timestamp': '2020-01-01', 'group': 'a', 'dollar_gains': 110},
                {'timestamp': '2020-01-01', 'group': 'b', 'dollar_gains': 90},
                {'timestamp': '2020-01-01', 'group': 'd', 'dollar_gains': 100}],
            'samp_df':pd.DataFrame([{'timestamp': '2020-01-01', 'group': 'a', 'dollar_gains': 100},
                {'timestamp': '2020-01-01', 'group': 'b', 'dollar_gains': 100},
                {'timestamp': '2020-01-01', 'group': 'c', 'dollar_gains': 110},
                {'timestamp': '2020-01-01', 'group': 'a', 'dollar_gains': 110},
                {'timestamp': '2020-01-01', 'group': 'b', 'dollar_gains': 90},
                {'timestamp': '2020-01-01', 'group': 'd', 'dollar_gains': 100}]),
            'samp_bad_input':'this should result in a failed test'}

@pytest.fixture(params=opt_dict.keys())
def df_opt(request):
    '''
    I haven't looked into why request works without a definition; it is some kind of pytest built-in for use with fixtures
    '''
    return opt_dict[request.param]

def test_sample(df_opt):
    if isinstance(df_opt,list):
        sample_df =  pd.DataFrame(df_opt)
    else:
        sample_df = df_opt
    assert isinstance(sample_df,pd.DataFrame), "somehow, we didn't end up with a dataframe"

Upvotes: 3

Related Questions