Why mocked class instantiated in module scope says it wasn't instantiated?

Viewed 37

I have a SlackClient class that I instantiate in my main program main.py in the module scope, to be re-used throughout the module (kind of like a singleton) to send messages via Slack. However I cannot get the test working that mocks out the client (I don't want the client sending messages to Slack every time I run my unit tests) and tests that the client was instantiated with the correct auth token.

The Slack client:

## slack_client.py

class SlackClient:
    def __init__(self, auth_token):
        pass

def instantiate_client():
    get_token_from_secrets = lambda: "mock-auth-token"
    return SlackClient(get_token_from_secrets())

The main program where the client is instantiated and used:

## main.py
from slack import instantiate_client

slack_client = instantiate_client()
# Then in this file I do some logic that calls the slack_client singleton.

The test file where I test the args SlackClient() was instantiated with:

import core
from unittest.mock import MagicMock, patch

@patch("core.slack_client")
def test_instantiation(slack_client):
    # This should succeed, but it fails
    slack_client.assert_called_once_with("mock-auth-token")

test_instantiation()

How can I test the arguments used to instantiate a class in the module scope?

1 Answers

I'm assuming you have some project structure that looks like this:

project
|_ core
|  |_ __init__.py
|  |_ slack_client.py
|_ main.py
|_ test.py

If so, you're patching the whole core.slack_client module when you should be patching the core.slack_client.SlackClient class.

The test file should look like:

from unittest.mock import MagicMock, patch

# patch the object with this namespace
@patch("core.slack_client.SlackClient")
def test_instantiation(slack_client):
    slack_client.assert_called_once_with("mock-auth-token")

test_instantiation()

If this works, you don't need to import core at all, as mock.patch will take care of patching that namespace. Alternatively, if I'm mistaken about your project structure, you might need to use mock.patch.object instead of mock.patch:

import core
from unittest.mock import MagicMock, patch

# patch the "SlackClient" object in the "core" module
@patch(core, "SlackClient")
def test_instantiation(slack_client):
    slack_client.assert_called_once_with("mock-auth-token")

test_instantiation()
Related