Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question: Is it possible to have a set of default test cases being overridden? #331

Open
MischaZihler opened this issue Jan 29, 2024 · 1 comment

Comments

@MischaZihler
Copy link

MischaZihler commented Jan 29, 2024

Hi,

I am looking into your interesting plugin and like the separation of concerns cases and tests.
My question is, if there is already a mechanism to override default cases based on attribute like a fixture for example:

a_condition = True

class cases_featureA_default:
    def case_int_1_success(self, env):
        return env

    def case_int_2_success(self):
        return 1

class cases_featureA_specific:
    def case_int_1_success(self):
        return 1

@fixture
def select_cases():
    if a_condition:
        return cases_featureA_specific

    return cases_featureA_default

class TestFeatureA:
    @parametrize_with_cases(
        "data",
        prefix="case_",
        glob="*success",
        cases=select_cases,
    )
    def test_A_1(self, data):
        assert sqrt(data) > 0

Or is this generally solved with a different logic?

Regards
Mischa

@smarie
Copy link
Owner

smarie commented Mar 9, 2024

Hi @MischaZihler , thanks for the interesting question !

If I understand correctly, your question relates to the general scenario of reusing a collection of cases (e.g. class CasesA across various tests (e.g. test_a and test_b), and you are interested in the possibility of keeping some flexibility, for example having one of the cases in CasesA overridden when the collection is used in test_b.

Such static flexibility could I guess, be done with subclasses (CasesAOverridden would inherit from CasesA and override only some of the cases) but currently this is not working:

from pytest_cases import parametrize_with_cases

class ACases(object):
    @staticmethod
    def case_static_overridden():
        return 1

    @staticmethod
    def case_static_inherited():
        return 2

    def case_normal_overridden(self):
        return 1

    def case_normal_inherited(self):
        return 2


class ACasesOverridden(ACases):
    @staticmethod
    def case_static_overridden():
        return 3

    def case_normal_overridden(self):
        return 3

@parametrize_with_cases("nb", cases=ACases)
def test_less_than_2(nb):
    assert nb <= 2

@parametrize_with_cases("nb", cases=ACasesOverridden)
def test_more_than_2(nb):
    assert nb >= 2

This leads to an error:

File "C:\_dev\python_ws\_Libs_OpenSource\python-pytest-cases\src\pytest_cases\case_parametrizer_new.py", line 914, in _extract_cases_from_module_or_class
    m_def_in_cls = cls.__dict__[m_name]
KeyError: 'case_normal_inherited'

So today, inheritance of cases between cases classes is currently not supported. I should make that more explicit in the error message.

A simple workaround for now is to list all cases that are inherited, explicitly:

class ACasesOverridden(ACases):
    @staticmethod
    def case_static_overridden():
        return 3

    def case_normal_overridden(self):
        return 3

    # For staticmethod and classmethod, you need to ref the __dict__
    case_static_inherited = ACases.__dict__['case_static_inherited']

    # For standard methods this is more straightforward
    case_normal_inherited = ACases.case_normal_inherited

So for all inherited methods, either you redefine them or you explicitly list them.
This seems to work fine from what I've tested.

Would this be what you're looking for or are you chasing a more complex use case ? Note that dynamically switching between parameters (for example based on a fixture's code content) is not possible in pytest, because pytest works with a static plan: (1) it collects all parameters and fixture definitions without executing anything, and (2) it executes the plan

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants