I am trying to use pytest to test some code that uses towerlib, an external Python package. Part of the functionality is that towerlib will dynamically add different attributes to its credential.py
module, and then the class imports its own module and looks for that attribute:
This is in towerlib.entities.credential.py
:
class Credential: # pylint: disable=too-few-public-methods
"""Credential factory to handle the different credential types returned."""
def __new__(cls, tower_instance, data):
try:
credential_type_name = tower_instance.get_credential_type_by_id(data.get('credential_type')).name
credential_type_name = ''.join(credential_type_name.split())
credential_type = f'{credential_type_name}Credential'
credential_type_obj = getattr(importlib.import_module('towerlib.entities.credential'), credential_type)
credential = credential_type_obj(tower_instance, data)
except Exception: # pylint: disable=broad-except
LOGGER.exception(
'Could not dynamically load credential with type : "%s", trying a generic one.', credential_type
)
credential = GenericCredential(tower_instance, data)
return credential
I assume this pytest does some sort of caching or other behavior that doesn't get updated when the module dynamically adds attributes to itself and reimports itself. Does anyone know if that's how pytest works, and if that's configurable with a flag or decorator on my test?
The function that I'm trying to test works correctly when run outside of pytest, but when running it in a pytest function, I'm getting the "Could not dynamically load credential with type" exception.
Running in the REPL:
>>> e2e.prepare_and_run_e2e(["my_device"], None, None, False, True, True, False, True)
^this returns a full results set with no errors.
My pytest function:
@pytest.mark.parametrize("device", get_all_e2e_devices())
def test_e2e(device):
results = e2e.prepare_and_run_e2e([device], None, None, False, True, True, False, True)
...
Ends up with:
ERROR credentials:credential.py:189 Could not dynamically load credential with type : "NetworkCredential", trying a generic one.
Traceback (most recent call last):
File "{my local path}\.venv\lib\site-packages\towerlib\entities\credential.py", line 186, in __new__
credential_type_obj = getattr(importlib.import_module('towerlib.entities.credential'), credential_type)
AttributeError: module 'towerlib.entities.credential' has no attribute 'NetworkCredential'
I tried running pytest with -p no:cacheprovider
, but that gives me the same result.
I don't want to patch/mock around the Credential because this is a special test in my application, and I want the test to actually get the towerlib Credential and talk to an external system. I also don't have the ability to change the somewhat confusing approach that towerlib is taking, since it's an external library, and I need to share this test across a team that will just be using the standard towerlib.