ediordna
ediordna

Reputation: 308

Mock an entire python module for django testcases

I'm currently trying to mock a credentials.py module which is not existent during the tests when running them with Gitlab Runner in the pipeline (credentials are on .gitignore). So in fact the "mock" has to create the credentials.py.

EDIT: I guess the problem is the django system check (https://docs.djangoproject.com/en/2.1/ref/checks/), which checks if all imports are available.

EDIT2: I found a way to prevent the import_error in testing environment. But I'm not sure if this is the attempt to choose in regards of writing good code, that's why I didn't use the answer-function. I found following stackoverflow question: Python: Mock a module without importing it or needing it to exist and used the recommendation in the first answer to change my code in views.py:

try:
   from battery_upgrade_web import credentials
except ImportError:
   from battery_upgrade_web import credentials_example as credentials

credentials_example is existing in Gitlab and is empty. This way it works to conduct all tests sucessfully in the Gitlab Runner.

My test_views.py looks like this:

@patch('battery_upgrade_web.views.BatteryUpgradeView.credentials', new=credentials_example)
class IndexViewTest(TestCase):

    @patch('battery_upgrade_web.views.BatteryUpgradeView.credentials', new=credentials_example)
    def setUp(self):
        # A client simulates a user interacting with the code at the view level
        # Lot of working mocks
        self.c = Client()

    @patch('battery_upgrade_web.views.credentials', new=credentials_example)
    def test_valid_data(self):
        resp = self.c.post('/', data={'parameter': 324})

My views.py:

from battery_upgrade_web import credentials

class BatteryUpgradeView(generic.TemplateView):
    def post(self, request, *args, **kwargs):
    #lot of code to execute

My problem is, that I can't only patch the variables in credentials.py but I have to patch the whole module and replace it with credentials_example.py. My solution above works locally with existing credentials.py, it also mocks the credentials.py and replaces it with my credentials_example.py during tests. But when I delete credentials.py, the test throws following error message when running >python web/manage.py test battery_upgrade_web:

Creating test database for alias 'default'...
Traceback (most recent call last):
File "web/manage.py", line 16, in <module>
    execute_from_command_line(sys.argv)
    # lot of tracebacks
File "C:\Users\e\AppData\Local\Continuum\anaconda2\envs\BatteryUpgrade36\lib\importlib\__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 994, in _gcd_import
File "<frozen importlib._bootstrap>", line 971, in _find_and_loal
File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 678, in exec_module
File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
File "C:\Users\e\Projects\BatteryUpgrade\web\battery_upgrade_web\urls.py", line 21, in <module>
from battery_upgrade_web.views import BatteryUpgradeView
File "C:\Users\e\Projects\BatteryUpgrade\web\battery_upgrade_web\views.py", line 11, in <module>
from battery_upgrade_web import credentials
ImportError: cannot import name 'credentials'

It seems like, there is an import of the module before the tests are starting properly. But how to mock this?

Upvotes: 1

Views: 267

Answers (1)

MaximeK
MaximeK

Reputation: 2071

I can give you a direction to work:

def setUp(self):
    model_mocked = MagicMock()
    model_mocked.credentials.return_value = {}
    modules = {'battery_upgrade_web': model_mocked}
    patch.dict('sys.modules', modules).start()

That will create a MagicMock add the credentials object and add it in the list of module you can import (sys.modules).

Not sure if it will work for you, but that the way to do it.

Upvotes: 1

Related Questions