pythonpytestpytest-mock

How do I use pytest-mock with modules and correct import order


I am totally new to pytest-mock. My project layout looks like this:

src
  pull
    app.py
  utils
    sftp.py
tests
  test_pull.py

In app.py I have this:

from src.utils.sftp import list_files

def handler():
    response = list_files()

In sftp.py I have this:

def list_files():
    ...

Trying to write a pytest which has list_files mocked.

from src.pull.app import handler

def test_pulling(mocker):
    mocker.patch('src.utils.sftp.list_files', return_value=["something"])

This however does not work because, I believe because the import inside app.py is evaluated first. It works, when I change app.py and do a local import.

def handler():
    from src.utils.sftp import list_files
    response = list_files()

Is there a better way to set this up? Ideally I don't want to be dependant on the import order - another developer might revert the local import and break the test.


Solution

  • Reloading the module will work and won't require other changes

    def test_pulling(monkeypatch):
        monkeypatch.setattr(src.utils.sftp, 'list_files', MagicMock(return_value="mock"))
        importlib.reload(src.pull.app)
        assert handler() == "mock"
    

    however, sometimes it's an indication that you may need to refactor and use some dependency injection, like

    def handler(lf=None):
        response = lf() if lf else list_files()
        return response
    

    and the test would be as simple as

    def test_pulling_2():
        assert handler(MagicMock(return_value="mock2")) == "mock2"