Reputation: 2706
imagine you have a module
from module.my_module import Foo
def my_function():
foo = Foo()
return whatewer_can_happen(foo)
that you want to test like this
class TestFoo:
def __init__():
pass
def dont_care():
return 9
def test_something():
with mock.patch('tested_module.Foo') as mocked_class:
mocked_class.side_effect = lambda *args: TestFoo(*args)
assert tested_module.my_function() == 'not important'
essentially as if you wanted to inject the TestFoo in place of Foo (example wont work ofc, only for illustration)
How to mock the Foo
class in the tested module without changing the tested code? If it is not possible, why - how are classes in python so different to functions that mocking them is not possible?
I am not looking for ways to do it differently(factory function/mocking only class functions directly etc)
It would in some cases just be helpful (and more readable) to be able to write mock classes and inject them for use into the tested module
Upvotes: 1
Views: 6551
Reputation: 16815
You can always provide your own mock instead of the default MagicMock
instance by passing it to the mock.patch
call:
def test_something():
with mock.patch('tested_module.Foo', TestFoo):
assert tested_module.my_function() == 'not important'
Note that you can't use side_effect
here, because your new object is not a Mock
object. Also, if you use the decorator version, there will be no extra argument for the mock:
@mock.patch('tested_module.Foo', TestFoo):
def test_something():
assert tested_module.my_function() == 'not important'
Here is the relevant part of the documentation:
If new is omitted, then the target is replaced with an
AsyncMock
if the patched object is an async function or aMagicMock
otherwise. Ifpatch()
is used as a decorator and new is omitted, the created mock is passed in as an extra argument to the decorated function. Ifpatch()
is used as a context manager the created mock is returned by the context manager.
Upvotes: 2