72

I have two files

spike.py

class T1(object):
    def foo(self, afd):
        return "foo"

    def get_foo(self):
        return self.foo(1)


def bar():
    return "bar"

test_spike.py:

from unittest import TestCase
import unittest
from mock import patch, MagicMock
from spike import T1, bar


class TestStuff(TestCase):
    @patch('spike.T1.foo', MagicMock(return_value='patched'))
    def test_foo(self):
        foo = T1().get_foo()
        self.assertEqual('patched', foo)

    @patch('spike.bar')
    def test_bar(self, mock_obj):
        mock_obj.return_value = 'patched'
        bar = bar()
        self.assertEqual('patched', bar)


if __name__ == "__main__":
    unittest.main()

When I run python test_spike.py, the first test case would pass, but the second would fail. and I switch to use nosetests test_spike.py, then both two are failed.

I don't understand how this happened? These cases supposed to pass all.

nnov
  • 489
  • 6
  • 14
LeoShi
  • 1,637
  • 2
  • 14
  • 24
  • Possible duplicate of [Python mock patch doesn't work as expected for public method](https://stackoverflow.com/questions/30987973/python-mock-patch-doesnt-work-as-expected-for-public-method) – funky-future May 24 '17 at 21:15

3 Answers3

61

Access bar using spike.bar. Imported bar is not affected by mock.patch.

from unittest import TestCase
import unittest
from mock import patch, MagicMock
from spike import T1
import spike # <----


class TestShit(TestCase):
    @patch('spike.T1.foo', MagicMock(return_value='patched'))
    def test_foo(self):
        foo = T1().get_foo()
        self.assertEqual('patched', foo)

    @patch('spike.bar')
    def test_bar(self, mock_obj):
        mock_obj.return_value = 'patched'
        bar = spike.bar() # <-----
        self.assertEqual('patched', bar)


if __name__ == "__main__":
    unittest.main()
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
falsetru
  • 357,413
  • 63
  • 732
  • 636
38

For test_foo you are not using patch correctly. You should be using it like this:

class TestFoo(TestCase):
@patch.object(T1, 'foo', MagicMock(return_value='patched'))
def test_foo(self):
    foo = T1().get_foo()
    self.assertEqual('patched', foo)

that gives me:

nosetests test_spike.py 
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

Now the second example does not work because you import bar function (get a reference to it) and then try to mock it. When you mock something you can't change what your variables hold (reference to original function). To fix this you should use @falsetru suggested method like:

from unittest import TestCase
import unittest
from mock import patch
import spike


class TestFoo(TestCase):
    @patch('spike.bar')
    def test_bar(self, mock_obj):
        mock_obj.return_value = 'patched'
        value = spike.bar()
        self.assertEqual('patched', value)


if __name__ == "__main__":
    unittest.main()

this gives me:

python test_spike.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

But when I try to run it with nose I get:

 nosetests test_spike.py
F
======================================================================
FAIL: test_bar (src.test_spike.TestFoo)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/zilva/envs/test/local/lib/python2.7/site-packages/mock/mock.py", line 1305, in patched
    return func(*args, **keywargs)
  File "/home/zilva/git/test/src/test_spike.py", line 11, in test_bar
    self.assertEqual('patched', value)
AssertionError: 'patched' != 'bar'

----------------------------------------------------------------------
Ran 1 test in 0.001s

FAILED (failures=1)

This happends because I am patching not in the right place. My directory structure is:

test/
└── src/
    ├── spike.py
    ├── test_spike.py
    └── __init__.py

and I run tests from src directory so I should be patching using path from project root directory like:

@patch('src.spike.bar')

and this would give me:

nosetests test_spike.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

or if I am at test directory:

nosetests src/test_spike.py
.
----------------------------------------------------------------------
Ran 1 test in 0.001s

OK
Aman Agrawal
  • 193
  • 1
  • 2
  • 14
Žilvinas Rudžionis
  • 1,954
  • 20
  • 28
28

To elaborate on the very helpful top answer, let me paraphrase the official documentation for unittest.mock.

a.py
    class SomeClass:
        ...

b.py
    import a
    from a import SomeClass
    def some_function():
        a.SomeClass()
        SomeClass()

If you write mock.patch('a.SomeClass'), this will affect the first line of some_function. If you write mock.patch('b.SomeClass'), this will affect the second line.

  • 2
    Thank you, this made the difference for me! instead of patching `core_module.core_function` I had to patch `module_that_im_testing.core_function`, since the module that I'm testing also imported the function. – Kalle Nijs Jan 09 '23 at 14:32
  • 1
    It's easiest to patch functions globally if you always call them with qualified names. For example, inside of `module_that_im_testing`, don't write `core_function(...)`, but instead write `core_module.core_function(...)`. That way, you don't have to patch `core_function` once for every module where it's used. – matthew-mcallister Jan 10 '23 at 17:49
  • 1
    ffs thank you. This wasn't very intuitive for me, but you made it very clear. – WakeskaterX Mar 14 '23 at 17:00