0

Most advice on Python mocking is couched in short snippets outside of the unit test framework. This one works find, I'm trying to follow this advice, but it's not successful as soon as I embed it in a proper unit test. For example, this code which produces the output in the comment at the end:

# foo.py
def some_fn():
    return 'some_fn'

class Foo( object ):
    def method_1( self ):
        return some_fn()

# bar.py (depends on foo.py)
import foo
class Bar( object ):
    def method_2( self ):
        tmp = foo.Foo()
        return tmp.method_1()

# test.py (tests bar.py)
import unittest
import bar
from mock import patch

class Test( unittest.TestCase ):
    def setUp( self ):
        pass
    def tearDown( self ):
        pass

    @patch( 'foo.some_fn' )
    def test_bar( self, mock_some_fn ):
        mock_some_fn.return_value = 'test-val-1'
        tmp = bar.Bar()
        print tmp.method_2()
        self.assertEqual( tmp.method_2(), 'test-val-1' )  # line 32
        mock_some_fn.return_value = 'test-val-2'
        self.assertEqual( tmp.method_2(), 'test-val-2' )

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

Which I run in PyDev and see:

Finding files... done.
Importing test modules ... done.

some_fn
======================================================================
FAIL: test_bar (test.foo.all.Test)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/python2.7/site-packages/mock.py", line 1201, in patched
    return func(*args, **keywargs)
  File "/home/russ/dev/workspace/python-mocking/test/foo/all.py", line 32, in test_bar
    self.assertEqual( tmp.method_2(), 'test-val-1' )
AssertionError: 'some_fn' != 'test-val-1'

----------------------------------------------------------------------
Ran 1 test in 0.002s

FAILED (failures=1)

Remove the unit test framework and this code runs fine (here, just the test.py part of the whole file):

...
# test.py (tests bar.py)
import bar
from mock import patch

@patch( 'foo.some_fn' )
def test_bar( mock_some_fn ):
  mock_some_fn.return_value = 'test-val-1'
  tmp = bar.Bar()
  print tmp.method_2()
  assert tmp.method_2() == 'test-val-1'
  mock_some_fn.return_value = 'test-val-2'
  assert tmp.method_2() == 'test-val-2'

which successfully produces, when run:

~/dev/workspace/python-mocking/test/foo $ python
Python 2.7.5 (default, Nov  3 2014, 14:26:24) 
...
>>> import all0
>>> all0.test_bar()
test-val-1

What additional must I do to make this behave in the unit test framework?

Russ Bateman
  • 18,333
  • 14
  • 49
  • 65
  • Sorry I cannot reproduce your issue. Try to use `self.assertEqual(tmp.method_2(), 'test-val-1')` instead of `assert ....` to see what is the difference. As far as I can see it should work perfectly .... I'm using `mock` in `unittesting` quite intensely in a lot of python versions (2.7, 3.2. and 3.4) without any issue like these. – Michele d'Amico Feb 12 '15 at 08:21
  • Thanks. I am amending my unit test code just as you ask and I make other smalls changes also to make the question clearer. I have several examples of this problem using different code snippets gathered from Googleland. I've been writing in Python for only 3 months. I'm a Java guy used to using mocking extensively in unit tests and so trying to get it down in Python. – Russ Bateman Feb 12 '15 at 15:16
  • Can you update the pyDev output too? It still reference the old code because in stack trace I read `assert tmp.method_2() == 'test-val-1'` – Michele d'Amico Feb 12 '15 at 15:30
  • BTW I'm sitting over tons of tests like the one you showed and I had any issue like that. – Michele d'Amico Feb 12 '15 at 15:34
  • Updated that--thanks. I don't understand your immediately previous comment: you are saying that you do not get the same thing? – Russ Bateman Feb 12 '15 at 15:58
  • Yes I cut and paste your code and I cannot reproduce the issue – Michele d'Amico Feb 12 '15 at 16:01
  • 1
    My best guess is something about Eclipse/PyDev: can you try to call the unittest version on a console by `python test.py`? I bet a dollar that it will work. – Michele d'Amico Feb 12 '15 at 16:17
  • I removed the dust from my Eclipse and tested your it on Eclipse too... It works like a charm. You should have forget something ... are you sure that `patch` work on the right `foo` module... Is there any other `foo` module in your python path? – Michele d'Amico Feb 12 '15 at 16:28
  • Thank you, no this is done in isolation. I confess, I don't know how to do this from the command line/console. I launch, import all and don't know what to do next (nothing I try works) whereas if I import all0, then execute all0.test_bar(), that does work. – Russ Bateman Feb 12 '15 at 20:04
  • 1
    Not from python console but from your command line shell. (I assume you are on windows) Go in the same directory where `test.py` is and type `python.exe test.py`... if not work replace `python.exe` by the full path of `python.exe`. BTW I would like point out that you should have missed something ... there is non way to reproduce it by cut and paste your example. – Michele d'Amico Feb 12 '15 at 20:38
  • python all.py and test.py works fine, so it's PyDev that's broken. Thanks very much, I guess we're done. – Russ Bateman Feb 12 '15 at 23:42

1 Answers1

0

The answer for me, a Python and PyDev neophyte, is that this is a PyDev problem. PyDev appears very touchy about how a project's unit testing is set up. I succeeded in making this code work by knitting together a new project set up exactly as this, including separate files.

python-mocking
+-- mocking_example
    +-- test
    |   +-- __init__.py
    |   `-- test.py
    +-- __init__.py
    +-- bar.py
    `-- foo.py

It might be worth noting that the structure above makes it so that, in test.py, bar must be imported thus:

import mocking_example.bar

and consumed that way, i.e.:

tmp = mocking_example.bar.Bar()
Russ Bateman
  • 18,333
  • 14
  • 49
  • 65
  • 1
    I think your issue is in python path configuration for your project: if you add `mocking_example` to your python path you can access to it without any problems. – Michele d'Amico Feb 25 '15 at 10:23
  • Yes, thanks, I see. I did not want to affect the portability of my PyDev project and had resisted that. This answers a closely related question: http://stackoverflow.com/questions/9249995/how-to-persist-pythonpath-setting-of-an-eclipse-pydev-project – Russ Bateman Feb 25 '15 at 15:09
  • 1
    I see that you are a java developer and so Eclipse is your home, but I would like to recommend to try PyCharm... is very great tool! – Michele d'Amico Feb 25 '15 at 15:16