Why python mock patch doesn't work? - python

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.

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()

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

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.

Related

Creating Unittest class inside function not working

In the attached script why 0 testcases are running
import unittest
def smg():
def add(x, y):
return x + y
class SimpleTest(unittest.TestCase):
def testadd1(self):
self.assertEquals(add(4, 5), 9)
if __name__ == '__main__':
unittest.main()
smg()
Gives
Ran 0 tests in 0.000s
What can be done to fix it kindly assist
You might be interested with unittest.TextTestRunner:
A basic test runner implementation that outputs results to a stream.
Sample usage:
However, should you want to customize the building of your test suite, you can do it yourself:
def suite():
suite = unittest.TestSuite()
suite.addTest(WidgetTestCase('test_default_widget_size'))
suite.addTest(WidgetTestCase('test_widget_resize'))
return suite
if __name__ == '__main__':
runner = unittest.TextTestRunner()
runner.run(suite())
Sample run for your case.
src.py
def add(x, y):
print("Add", x, y)
return x + y
test_src.py
import unittest
from src import add
class SimpleTest(unittest.TestCase):
def testadd1(self):
self.assertEqual(add(4, 5), 9)
if __name__ == '__main__':
unittest.main()
Running tests is as how it's done normally
$ python test_src.py # Using unittest
Add 4 5
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
$ pytest -q # Using pytest
.
1 passed in 0.06s
Now if you want to manually call it via function.
run_tests.py
import unittest
import test_src
def suite():
suite = unittest.TestSuite()
suite.addTest(test_src.SimpleTest('testadd1'))
return suite
def run():
runner = unittest.TextTestRunner()
runner.run(suite())
# run() # Uncomment if you want to try to run it as a script e.g. <python run_tests.py>
You can now just import the file and call run() whenever you need:
$ python3
>>> import run_tests
>>> run_tests.run()
Add 4 5
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
>>>
Take out the code inside of smg function and then run tests from your command-line with python -m unittest <Your Filename>.py.
Your code will look like this:
import unittest
def add(x, y):
return x + y
class SimpleTest(unittest.TestCase):
def testadd1(self):
self.assertEquals(add(4, 5), 9)
if __name__ == '__main__':
unittest.main()
Also you may get a deprecation warning for assertEquals. You might want to change it to assertEqual instead.

Python assertRaises on user-defined exceptions

The following question was triggered by the discussion in this post.
Assume two files (foobar.py and foobar_unittest.py). File foobar.py contains a class (FooBar) with two functions (foo and bar). Function bar raises a built-in exception, function foo a user-defined exception.
# foobar.py
class MyException(Exception):
pass
class FooBar:
def __init__(self):
pass
def bar(self):
raise ValueError('Hello World.')
def foo(self):
raise MyException('Hello World.')
.
# foobar_unittest.py
import unittest
import foobar as fb
class MyException(Exception):
pass
class FooBarTestCases(unittest.TestCase):
def test_bar(self):
with self.assertRaises(ValueError):
fb.FooBar().bar()
def test_foo(self):
with self.assertRaises(MyException):
fb.FooBar().foo()
if __name__ == '__main__':
unittest.main()
When running unit-test on foobar.py, why does the function raising the user-defined exception (foo) fail to pass the test?
>>> python2.7 foobar_unittest.py
.E
======================================================================
ERROR: test_foo (__main__.FooBarTestCases)
----------------------------------------------------------------------
Traceback (most recent call last):
File "foobar_unittest.py", line 11, in test_foo
fb.FooBar().foo()
File "/a_path/foobar.py", line 9, in foo
raise MyException('Hello World.')
MyException: Hello World.
----------------------------------------------------------------------
Ran 2 tests in 0.000s
FAILED (errors=1)
import MyException from foobar, don't redefine it.
import unittest
from foobar import MyException
import foobar as fb
class FooBarTestCases(unittest.TestCase):
def test_bar(self):
with self.assertRaises(ValueError):
fb.FooBar().bar()
def test_foo(self):
with self.assertRaises(MyException):
fb.FooBar().foo()
if __name__ == '__main__':
unittest.main()
This code should work now as
..
----------------------------------------------------------------------
Ran 2 tests in 0.001s
OK
Be aware that the same happens (happened to me) if you use reload to import your exceptions.
In my unittests I have the relevant imports like
from importlib import reload
import foobar
reload(foobar)
from foobar import MyException
This does not work, too, for whatever reason. Writing this just as
from foobar import MyException
will work. Then, of course, you have to reload the modules yourself.
In case you wonder why I am using reload: How do I unload (reload) a Python module?.

Skipping unittest with decorator in python

I'm writing some unittest and found a rather curious behavior that nearly burned me.
The following test:
import unittest
class Test(unittest.TestCase):
#unittest.skip('Not ready yet')
def test_A(self):
self.assertTrue(False)
#unittest.skip
def test_B(self):
self.assertTrue(False)
def test_C(self):
self.assertTrue(False)
if __name__ == '__main__':
unittest.main()
results in:
test_A (__main__.Test) ... skipped 'Not ready yet'
test_B (__main__.Test) ... ok
test_C (__main__.Test) ... FAIL
======================================================================
FAIL: test_C (__main__.Test)
----------------------------------------------------------------------
Traceback (most recent call last):
File "./test.py", line 13, in test_C
self.assertTrue(False)
AssertionError: False is not true
----------------------------------------------------------------------
Ran 3 tests in 0.000s
Using the decorator unittest.skip empty does skip the test but then reports it as passed. Therefore this skipped test could be easily forgotten the next day and stay in the skip state forever. What is the reason behind this skip, but report pass behavior?
In case it matters:
Python: 3.4.3 | Anaconda 2.3.0 (64-bit)
OS: RHEL 6.7
#decorator
def f(): ...
is equivalent to
def f(): ...
f = decorator(f)
and
#decorator(...)
def f(): ...
is equivalent to
def f(): ...
f = decorator(...)(f)
That means when you forget the skip reason, you get the effects of
def test_B(self): ...
test_B = unittest.skip(test_B)
The test method is passed as the skip reason, and the returned test decorator is assigned to test_B. When unittest tries to run test_B, the test decorator reports no assertion failures, so unittest thinks it's a passing test.
The inequivalence of #decorator and #decorator() is one of Python's design warts, but there isn't much we can do about it.

How to use Python mocking in a unit test

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?
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()

Running unittest.main() from a module?

I wrote a little function that dynamically defines unittest.TestCase classes (trivial version below).
When I moved it out of the same source file into its own module, I can't figure out how to get unittest to discover the new classes. Calling unittest.main() from either file doesn't execute any tests.
factory.py:
import unittest
_testnum = 0
def test_factory(a, b):
global _testnum
testname = 'dyntest' + str(_testnum)
globals()[testname] = type(testname, (unittest.TestCase,), {'testme': lambda self: self.assertEqual(a, b)})
_testnum += 1
def finish():
unittest.main()
someotherfile.py:
from factory import test_factory, finish
test_factory(1, 1)
test_factory(1, 2)
if __name__ == '__main__':
finish()
Output:
----------------------------------------------------------------------
Ran 0 tests in 0.000s
OK
So it doesn't execute any tests.
Note that keeping it all in the same file works as expected:
import unittest
_testnum = 0
def test_factory(a, b):
global _testnum
testname = 'dyntest' + str(_testnum)
globals()[testname] = type(testname, (unittest.TestCase,), {'testme': lambda self: self.assertEqual(a, b)})
_testnum += 1
test_factory(1, 1)
test_factory(1, 2)
if __name__ == '__main__':
unittest.main()
Output (as expected):
.F
======================================================================
FAIL: testme (__main__.dyntest1)
----------------------------------------------------------------------
Traceback (most recent call last):
File "partb.py", line 11, in <lambda>
globals()[testname] = type(testname, (unittest.TestCase,), {'testme': lambda self: self.assertEqual(a, b)})
AssertionError: 1 != 2
----------------------------------------------------------------------
Ran 2 tests in 0.008s
FAILED (failures=1)
How I use my test_factory() function such that I can execute all of the TestCase objects it defines from a separate source file?
The general idea (what unittest.main does for you) is:
suite = unittest.TestLoader().loadTestsFromTestCase(SomeTestCase)
unittest.TextTestRunner(verbosity=2).run(suite)
as per http://docs.python.org/library/unittest.html?highlight=unittest#module-unittest . Your test cases are hidden in globals() by the test_factory function, so just do a dir(), find the globals that are instances of unittest.TestCase (or ones with names starting with 'dyntest', etc), and just build your suite that way and run it.
By default, unittest.main() looks for unit TestCase objects in the main module. The test_factory creates the TestCase objects in its own module. That's why moving it outside of the main module causes the behavior you see.
Try:
def finish():
unittest.main(module=__name__)

Categories