91

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.

1

3 Answers 3

71

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()
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, it works for python test_spike.py, but why it still not works for nosetests test_spike.py?
@LeoShi, Sorry, I don't know about that.
65

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.

3 Comments

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.
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.
ffs thank you. This wasn't very intuitive for me, but you made it very clear.
45

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.