Recently, we ran into an issue at work where implementing pytest in a project just wasn't working out. After some time of on-and-off troubleshooting, we discovered that it was because this project imports a module from another internal project that relies on cli arguments being present or not present. It does different things depending on the circumstances. Changing this module was impossible. It was already a workaround to ensure compatibility with client products and worked exactly as intended.

So how can we go about testing our code?

Enter some fancy manipulation with Python Fixtures! (After a week or so of troubleshooting other options and Typings bugs.)

BONUS:

  • Python < 3.6.2 has some known issues with Typings . I was testing locally on Python 3.6.0 for some compatibility testing earlier and this defintely ate up a portion of my time.
  • pytest > 5.0.1 currently has bugs with how it interacts with VSCode. Since the majority of our developers work on VSCode, its basically meant future versions are currently broken. We'll be updating once a patch comes but pinning pytest for now.

Solution

Global conftest.py fixture which will import the offending module ( BADMODULE ) from our company repo. Then patch the offending method, argparse.parse in this case, to disallow passing params.

def fixed_function(self):
    # do good stuff
    # kwargs.get('args', '')
    # instead of kwargs.get('args', None)
    # which defaults to pulling cli args
    print("Good Stuffs")
    return ['Good Stuff']

@pytest.fixture(scope="session")
def global_fixture():
    from BADPROJECT.badModule import BadModule
    patch.object(BadModule, 'BadFunction')
    BadModule.BadFunction = fixed_function

Then, a module level fixture in each test that imports BADMODULE. This module level fixture can't freely pass imported objects into the actual test cases so the compromise between ease of use and clear understanding was to add imported items to pytest.<> global variables.

@pytest.fixture(scope="module", autouse=True)
def ImportModules():
    from BADPROJECT.badModule import BadModule
    pytest.BadModule = BadModule

Now, to use the successfully imported module, just use pytest.BadModule instead of BadModule like you normally would.