Andry
Andry

Reputation: 16845

Python unittest patching is not working as expected

I have the following project structure:

root/
|-mylib/
  |-tests/
  | |-__init__.py
  | |-test_trend.py
  |-__init__.py
  |-data.py
  |-trend.py

In trend.py:

from mylib.data import get_item

def get_trend(input: str):
    item = get_item(input)
    return f"Trend is: {item}"

Inside data.py:

def get_item(id):
    print("ORIGINAL")
    return get_item_from_db(id)

Testing

I want to test get_trend in isolation, therefore I patch get_item inside: test_trend.py:

import unittest
from unittest.mock import patch
from mylib.trend import get_trend

def p__get_item(input):
    return 0

class MyTestCase(unittest.TestCase):
    @patch("mylib.data.get_item", new=p__get_item)
    def test_trend(self):
        v = get_trend()
        self.assertEqual(v, "Trend is: 0")

But when I run the tests (the command is run from inside the root directory):

python -m unittest discover

I see that the log shows that the original get_item is called. The test is failing of course.

What am I doing wrong?


Attempt 1

If I try this other flavor of the patch APIL

class MyTestCase(unittest.TestCase):
    @patch("mylib.data.get_item")
    def test_trend(self, mocked_fun):
        mocked_fun.return_value = 0
        v = get_trend()
        self.assertEqual(v, "Trend is: 0")

It still does not work. In the console log I can see ORIGINAL being printed and the test fails.

Experiment 1

If I change the target in @patch to a non existing attribute like:

@patch("mylib.data.non_existing", new=p__get_item)

I actually get an error from the library saying the module does not contain such attribute. So, it seems like mylib.data.get_item is correctly being targeted, but still the patching is not happening.

Upvotes: 3

Views: 467

Answers (1)

annonymous
annonymous

Reputation: 816

Explanation

get_item is called in mylib.trend module, however, you patched it in mylib.data module, which is the wrong place. patch works by replacing objects on import. In this case, you want to patch get_item inside mylib.trend module not mylib.data.

Solution

class MyTestCase(unittest.TestCase):
    @patch("mylib.trend.get_item")
    def test_trend(self, mocked_get_item):
        mocked_get_item.return_value = 0
        ...

Notes

Further explanation on where to patch can be found on the official python documentation here.

Upvotes: 2

Related Questions