DEV Community

Cover image for How to debug your Python mocks or imports
Sophie Warner
Sophie Warner

Posted on

How to debug your Python mocks or imports

Hands up if you've spent 20 minutes making a feature then 2 hours testing it 🙋. I recently struggled with mocking an object declared globally in a file I was testing. It was making all my tests fail because its __init__ method made a call to AWS which wouldn't have the right credentials everywhere the test was run.

Because of my relatively complicated directory structure, I was struggling to find exactly the right module to mock. You might have read this relatively simple where to mock section in the unittest docs but here is how to figure where to mock out for any scenario, no matter how complicated.

I first commented out the call to the AWS client in MyClient.py. As it was called globally the NoCredentialsError was causing all my tests to fail before any methods or tests were executing 😵‍💫. Then I inspected the sys.modules dict containing all the modules that your test file can see and the path to them.

# test_app.py
import sys
import json

def test_lambda_handler():
    print(sys.modules)

    assert True == False
Enter fullscreen mode Exit fullscreen mode

It'll show your own modules at the bottom.

...
    "boto3.resources.factory": <module "boto3.resources.factory" from "/usr/local/lib/python3.11/site-packages/boto3/resources/factory.py">,
    "boto3.session": <module "boto3.session" from "/usr/local/lib/python3.11/site-packages/boto3/session.py">,
    "boto3": <module "boto3" from "/usr/local/lib/python3.11/site-packages/boto3/__init__.py">,
    "hello_world.util": <module "hello_world.util" from "/Users/sophiewarner/repos/project/hello_world/util.py">,
    "hello_world.service.my_client": <module "hello_world.service.my_client" from "/Users/sophiewarner/repos/project/hello_world/service/slack_client.py">,
    "hello_world.app": <module "hello_world.app" from "/Users/sophiewarner/repos/project/hello_world/app.py">
}
Enter fullscreen mode Exit fullscreen mode

From here you can just replace what sys thinks is at that path with a mock.

# test_app.py
import sys
import json
from unittest.mock import MagicMock

sys.modules["hello_world.service.my_client"] = MagicMock()
from hello_world import app
event = {...}

def test_lambda_handler(event):
    ret = app.lambda_handler(event, "")
    assert ret["statusCode"] == 200
Enter fullscreen mode Exit fullscreen mode

This will update sys.modules for your entire python session. An improvement on this would be patching it just for one test.

# test_app.py
import sys
import json
from unittest.mock import MagicMock, patch

event = {...}

with patch.dict("sys.modules", {"hello_world.service.my_client": MagicMock()}):
def test_lambda_handler(event):
    from hello_world import app
    ret = app.lambda_handler(event, "")
    assert ret["statusCode"] == 200
Enter fullscreen mode Exit fullscreen mode

✅ No more mocking and patching headaches.

Image of AssemblyAI

Automatic Speech Recognition with AssemblyAI

Experience near-human accuracy, low-latency performance, and advanced Speech AI capabilities with AssemblyAI's Speech-to-Text API. Sign up today and get $50 in API credit. No credit card required.

Try the API

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Discover a treasure trove of wisdom within this insightful piece, highly respected in the nurturing DEV Community enviroment. Developers, whether novice or expert, are encouraged to participate and add to our shared knowledge basin.

A simple "thank you" can illuminate someone's day. Express your appreciation in the comments section!

On DEV, sharing ideas smoothens our journey and strengthens our community ties. Learn something useful? Offering a quick thanks to the author is deeply appreciated.

Okay