Currently dealing with some legacy python code. I need to test one of the functions, function foo . Now to do that I need to mock the return value given by another function that's called by foo which is function bar.
However both of these are outside of my testing package and when I try to mock out bar, foo is unaffected. Is there a way of doing this without moving everything into a class? (which is preferred, but it's legacy code).
A simple example:
foo_and_bar_package/foo_and_bar_module.py
def bar():
return 5
def foo():
return 2 * bar()
test_foo_and_bar_module.py
from unittest import TestCase
from unittest.mock import patch
from foo_and_bar_package.foo_and_bar_module import foo
class TestFoo(TestCase):
#patch('foo_and_bar_package.foo_and_bar_module.bar')
def test_foo(self, mock_bar):
mock_bar.return_value = 2
self.assertEqual(foo.foo(), 2 * mock_bar.return_value)
Related
what is the easiest way to solve the following problem in extending/altering the functionality of a third party library?
The library offers a class LibraryClass with a function func_to_be_changed. This function has a local variable internal_variable which is the instance of another class SimpleCalculation of that library. I created a new class BetterCalculation in my own module and now want LibraryClass.func_to_be_changed to use an instance of this new class.
# third party library
from third_party_library.utils import SimpleCalculation
class LibraryClass:
def func_to_be_changed(self, x):
# many complicated things go on
internal_variable = SimpleCalculation(x)
# many more complicated things go on
The easiest solution would be, to just copy the code from the third party library, subclass the LibraryClass and overwrite the function func_to_be_changed:
# my module
from third_party_library import LibraryClass
class BetterLibraryClass(LibraryClass):
def func_to_be_changed(self, x):
"""This is an exact copy of LibraryClass.func_to_be_changed."""
# many complicated things go on
internal_variable = BetterCalculation(x) # Attention: this line has been changed!!!
# many more complicated things go on
However, this involves copying of many lines of code. When a new version of the third party class improves on code that was copied without modification, this modifications need to be incorporated manually by another copying step.
I tried to use unittest.mock.patch as I know that the following two snippets work:
# some script
from unittest.mock import patch
import third_party_library
from my_module import BetterCalculation
with patch('third_party_library.utils.SimpleCalculation', BetterCalculation):
local_ = third_party_library.utils.SimpleCalculation(x) # indeed uses `BetterCalculation`
def foo(x):
return third_party_library.utils.SimpleCalculation(x)
with patch('third_party_library.utils.SimpleCalculation', BetterCalculation):
local_ = foo(x) # indeed uses `BetterCalculation`
However, the following does not work:
# some script
from unittest.mock import patch
from third_party_library.utils import SimpleCalculation
from my_module import BetterCalculation
def foo(x):
return SimpleCalculation(x)
with patch('third_party_library.utils.SimpleCalculation', BetterCalculation):
local_ = foo(x) # does not use `BetterCalculation`
# this works again
with patch('__main__.SimpleCalculation', BetterCalculation):
local_ = foo(x) # indeed uses `BetterCalculation`
Therefore, the following won`t work either:
# my module
from unittest.mock import patch
from third_party_library import LibraryClass
from my_module import BetterCalculation
class BetterLibraryClass(LibraryClass):
def func_to_be_changed(self, x):
with patch(
'third_party_library.utils.SimpleCalculation',
BetterCalculation
):
super().func_to_be_changed(x)
Is there an elegant pythonic way to do this? I guess this boils down to the question: What is the equivaltent of __main__ in the last code snippet that needs to replace third_party_library.utils?
Some context
The first string argument in the patch function can have two different meanings depending on the situation. In the first situation the described object has not been imported and is unavailable to the program which would, therefore, result in a NameError without the mocking. However, in the question, an object needs to be overwritten. Therefore, it is available to the program and not using patch would not result in an error.
Disclaimer
I might have used the complete wrong language in here, as for sure there are precise python terms for all the described notions.
Overwriting an object
As shown in the question, the locally imported SimpleCalculation can be overwritten with __main__.SimpleCalculation. Therefore, it is important to remember that you need to tell patch the path to the local object and not how that same object would be imported in the current script.
Let's assume the following module:
# thirdpartymodule/__init__.py
from .utils import foo
def local_foo():
print("Hello local!")
class Bar:
def __init__(self):
foo()
local_foo()
and
# thirdpartymodule/utils.py
def foo():
print("third party module")
We want to override the functions foo and local_foo. But we don't want to override any functions, we want to override the functions foo and local_foo in the context of the file thirdpartymodule/__init__.py. It is unimportant that the function foo enters the context of the file via an import statement, while local_foo is defined locally. So we want to override the functions in the context of thirdpartymodule.foo and thirdpartymodule.local_foo. The context thirdpartymodule.utils.foo is not important here and won't help us. The following snippet illustrates that:
from unittest.mock import patch
from thirdpartymodule import Bar
bar = Bar()
# third party module
# Hello local!
def myfoo():
print("patched function")
with patch("thirdpartymodule.foo", myfoo):
bar = Bar()
# patched function
# Hello local!
# will not work!
with patch("thirdpartymodule.utils.foo", myfoo):
bar = Bar()
# third party module
# Hello local!
with patch("thirdpartymodule.local_foo", myfoo):
bar = Bar()
# third party module
# patched function
In the hypothetical module of the question we first need to assume that the class LibraryClass is defined in the file third_party_library/library_class.py. Then, we want to override third_party_library.library_class.SimpleCalculation and the correct patch would be:
# my module
from unittest.mock import patch
from third_party_library import LibraryClass
from my_module import BetterCalculation
class BetterLibraryClass(LibraryClass):
def func_to_be_changed(self, x):
with patch(
'third_party_library.library_class.SimpleCalculation',
BetterCalculation
):
super().func_to_be_changed(x)
I am trying to mock two methods within a single function call. Below I have the method entry_point which then calls foo, which in-turn calls bar.
I want to ensure that both foo and bar are called, and I also want to inspect their arguments to see what the types and values are of the passed arguments.
foo.py
class MyClass:
def entry_point(self):
self.foo(x=2)
def foo(self, x):
self.bar(y=x*3)
def bar(self, y):
return y**2
test_foo.py
import unittest
from unittest.mock import patch
from foo import MyClass
class TestMyClass(unittest.TestCase):
def test_nested_patch(self):
mc = MyClass()
# I would like to find out what arguments are passed
# in to both foo() and bar()
with patch.object(mc, "foo") as mock1:
with patch.object(mc, "bar") as mock2:
mc.entry_point()
# PASSES
mock1.assert_called_once()
# FAILS HERE: "bar" not called
mock2.assert_called_once()
# ADDITONAL: I want to see the function arguments
_, kwargs1 = mock1.call_args
_, kwargs2 = mock2.call_args
self.assertEqual(kwargs1["x"], 2)
self.assertEqual(kwargs2["y"], 6)
AssertionError: Expected 'bar' to have been called once. Called 0 times.
I have tried it a few different ways, but the above code seemed to be the cleanest way to explain my situation.
I recognize I could get around this by calling mc.entry_point() within two different (non-nested) patch context managers (one for foo, one for bar) but that is not as clean and doesn't really give me full control over the function calls.
This is my probject structure, and this problem is a MCVE.
wrx_test
use_mock
init.py
A.py
B.py
use_mock.py
A.py
def do_something():
print 'a i do something'
return 10
B.py
from wrx_test.use_mock.A import do_something
class B(object):
def b_do_something(self):
x = do_something()
print x
return x
use_mock.py
from mock import patch
from unittest import TestCase
from wrx_test.use_mock.B import B
class TestUseMock(TestCase):
#patch('wrx_test.use_mock.A.do_something')
def test_use_mock(self, mock_do_something):
from wrx_test.use_mock.A import do_something
mock_do_something.return_value = 4
print do_something()
B().b_do_something()
In use_mock.py i set the mocked method return_value equal 4, but it doesn't work as excepted. How can i make the mocked method A.do_something work correctly in module B, and i wonder now the reason.
result
a i do something
10
I think the way mock works is that you designate a function in a module to be replaced by your mock when this function is used in another call. Example: patch('A.do_something) would mean that if you test a function that internally calls A.do_something(), it will be replaced inside that call. But here you're directly executing that function.
I also don't see any useful case for doing this. If you want to call the mock directly, do: mock_do_something()
I like small, self-contained modules, that sometimes contain a single class or a single function, e.g.
def decorator(function):
return function
By convention, I use full, absolute imports only, e.g.
# Yes
import module
module.function()
# No
from module import function
function()
Together, this might become annoyingly verbose, e.g.
import decorator
#decorator.decorator
def function():
pass
So I like to export things other than modules via sys.modules, e.g.
import sys
def decorator(function):
return function
sys.modules[__name__] = decorator
And then,
import decorator
#decorator
def function():
pass
This was the intro; whether I should do this or not is not the issue. The issue is this strange behaviour:
# foo.py
import sys
x = 1
def foo():
print(x)
sys.modules[__name__] = foo
And then,
>>> import foo
>>> foo()
None
And stranger still, this only happens in Python 2.7; in Python 3.4 it works as expected! My question is, why does this happen, and how can I make this work in Python 2.7?
Thanks.
The question is in context of unit testing.
I have created an instance of the class I am testing, and trying to test one of its methods. The method uses data it gets from different class defined in separate module. I am going to mock that module.
How I can access my instance's name space - I have to do it before running the method I am testing, to mock module which contain definition of the class my method is getting data from?
I am going to create an example here which I think parallels what you are trying to do.
Say you have some class that we'll call Data that is defined in the module foo. The foo module imports bar and a method of foo.Data calls bar.get_data() to populate itself.
You want to create a module test that will create an instance of foo.Data, but instead of using the actual module bar you want that instance to use a mocked version of this.
You can set this up by importing foo from your test module, and then rebinding foo.bar to your mocked version of the module.
Here is an example of how this might look:
bar.py:
def get_data():
return 'bar'
foo.py:
import bar
class Data(object):
def __init__(self):
self.val = bar.get_data()
if __name__ == '__main__':
d = Data()
print d.val # prints 'bar'
test.py:
import foo
class bar_mock(object):
#staticmethod
def get_data():
return 'test'
if __name__ == '__main__':
foo.bar = bar_mock
d = foo.Data()
print d.val # prints 'test'
Although this will get you by for a simple test case, you are probably better off looking into a mocking library to handle this for you.