Is there a clean way to patch an object so that you get the assert_call* helpers in your test case, without actually removing the action?
For example, how can I modify the #patch line to get the following test passing:
from unittest import TestCase
from mock import patch
class Potato(object):
def foo(self, n):
return self.bar(n)
def bar(self, n):
return n + 2
class PotatoTest(TestCase):
#patch.object(Potato, 'foo')
def test_something(self, mock):
spud = Potato()
forty_two = spud.foo(n=40)
mock.assert_called_once_with(n=40)
self.assertEqual(forty_two, 42)
I could probably hack this together using side_effect, but I was hoping there would be a nicer way which works the same way on all of functions, classmethods, staticmethods, unbound methods, etc.
Similar solution with yours, but using wraps:
def test_something(self):
spud = Potato()
with patch.object(Potato, 'foo', wraps=spud.foo) as mock:
forty_two = spud.foo(n=40)
mock.assert_called_once_with(n=40)
self.assertEqual(forty_two, 42)
According to the documentation:
wraps: Item for the mock object to wrap. If wraps is not None then
calling the Mock will pass the call through to the wrapped object
(returning the real result). Attribute access on the mock will return
a Mock object that wraps the corresponding attribute of the wrapped
object (so attempting to access an attribute that doesn’t exist will
raise an AttributeError).
class Potato(object):
def spam(self, n):
return self.foo(n=n)
def foo(self, n):
return self.bar(n)
def bar(self, n):
return n + 2
class PotatoTest(TestCase):
def test_something(self):
spud = Potato()
with patch.object(Potato, 'foo', wraps=spud.foo) as mock:
forty_two = spud.spam(n=40)
mock.assert_called_once_with(n=40)
self.assertEqual(forty_two, 42)
This answer address the additional requirement mentioned in the bounty from user Quuxplusone:
The important thing for my use-case is that it work with #patch.mock, i.e. that it not require me to insert any code in between my constructing of the instance of Potato (spud in this example) and my calling of spud.foo. I need spud to be created with a mocked-out foo method from the get-go, because I do not control the place where spud is created.
The use case described above could be achieved without too much trouble by using a decorator:
import unittest
import unittest.mock # Python 3
def spy_decorator(method_to_decorate):
mock = unittest.mock.MagicMock()
def wrapper(self, *args, **kwargs):
mock(*args, **kwargs)
return method_to_decorate(self, *args, **kwargs)
wrapper.mock = mock
return wrapper
def spam(n=42):
spud = Potato()
return spud.foo(n=n)
class Potato(object):
def foo(self, n):
return self.bar(n)
def bar(self, n):
return n + 2
class PotatoTest(unittest.TestCase):
def test_something(self):
foo = spy_decorator(Potato.foo)
with unittest.mock.patch.object(Potato, 'foo', foo):
forty_two = spam(n=40)
foo.mock.assert_called_once_with(n=40)
self.assertEqual(forty_two, 42)
if __name__ == '__main__':
unittest.main()
If the method replaced accepts mutable arguments which are modified under test, you might wish to initialize a CopyingMock* in place of the MagicMock inside the spy_decorator.
*It's a recipe taken from the docs which I've published on PyPI as copyingmock lib
For those who don't mind using side_effect, here's a solution with a few pros:
Uses decorator syntax
Patches an unbound method, which I find more versatile
Requires inclusion of the instance in the assertion
class PotatoTest(TestCase):
#patch.object(Potato, 'foo', side_effect=Potato.foo, autospec=True)
def test_something(self, mock):
spud = Potato()
forty_two = spud.foo(n=40)
mock.assert_called_once_with(spud, n=40)
self.assertEqual(forty_two, 42)
You are describing the same question than Python mock: wrap instance method. My solution in https://stackoverflow.com/a/72446339/9230828 can be applied as following: Put wrap_object somewhere, e.g. into wrap_object.py:
# Copyright (C) 2022, Benjamin Drung <bdrung#posteo.de>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import contextlib
import typing
import unittest.mock
#contextlib.contextmanager
def wrap_object(
target: object, attribute: str
) -> typing.Generator[unittest.mock.MagicMock, None, None]:
"""Wrap the named member on an object with a mock object.
wrap_object() can be used as a context manager. Inside the
body of the with statement, the attribute of the target is
wrapped with a :class:`unittest.mock.MagicMock` object. When
the with statement exits the patch is undone.
The instance argument 'self' of the wrapped attribute is
intentionally not logged in the MagicMock call. Therefore
wrap_object() can be used to check all calls to the object,
but not differentiate between different instances.
"""
mock = unittest.mock.MagicMock()
real_attribute = getattr(target, attribute)
def mocked_attribute(self, *args, **kwargs):
mock.__call__(*args, **kwargs)
return real_attribute(self, *args, **kwargs)
with unittest.mock.patch.object(target, attribute, mocked_attribute):
yield mock
Then you can write following unit test:
from unittest import TestCase
from wrap_object import wrap_object
class Potato:
def foo(self, n):
return self.bar(n)
def bar(self, n):
return n + 2
class PotatoTest(TestCase):
def test_something(self):
with wrap_object(Potato, 'foo') as mock:
spud = Potato()
forty_two = spud.foo(n=40)
mock.assert_called_once_with(n=40)
self.assertEqual(forty_two, 42)
I did it with a bit another way because IMO mocking is preferable over patching
from unittest.mock import create_autospec
mocked_method = create_autospec(
spec=my_method,
spec_set=True,
# Will implement a real behavior rather than return a Mock instance
side_effect=*a, **kw: my_method.do_something(*a, **kw))
mocked_object.do_something()
mocked_object.assert_called_once()
Related
I started learning Selenium and I am curious how to realize behavior from the PythonOrgSearch class which inherits from unittest.TestCase. Namely, each method which starts with test_ will be called automatically after initialization. I know this behavior is implemented in TestCase but I am interested in how to make something similar. Is there a design pattern that will take care of this?
And one bonus question, what is the point of assert True, since the condition is always True
import unittest
from selenium import webdriver
class PythonOrgSearch(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Chrome("C:\chorme\chromedriver.exe")
self.driver.get("http://www.python.org")
def test_example(self):
print("Test")
assert True
def not_test(self):
print("Not a test")
def tearDown(self):
self.driver.close()
if __name__ == "__main__":
unittest.main()
You can do what you want with a metaclass which can customize the construction of your own classes. This a very powerful and general technique and arguably a Python design pattern.
Below is an example of it being applied to what you want to do. The metaclass' __new__() method looks through the contents of the class being defined—which is when it gets called—and looks for callable attributes whose names start with test_. After doing that, it defines __init__() and post_init() methods and makes them part of the class. The former calls the latter method which then iteratively calls all the methods defined that had matching names.
class MyMetaClass(type):
""" Create class that calls an added post_init() method which in turn calls
all method's whose names start with "test_".
"""
def __new__(meta, classname, bases, classdict):
# Get any class __init__() method defined.
class_init = classdict.get('__init__', lambda *_, **__: None)
test_funcs = [value for key, value in classdict.items()
if key.startswith('test_') and callable(value)]
def __init__(self, *args, **kwargs):
print('In metaclass generated __init__()')
class_init(self, *args, **kwargs) # Call class' __init__() method.
self.post_init()
def post_init(self):
print('In metaclass generated post_init()')
for method in test_funcs:
print(f'calling {classname}.{method.__name__}()')
method(self)
classdict.update({'__init__': __init__, # Attach methods to class.
'post_init': post_init})
return type.__new__(meta, classname, bases, classdict)
class Example(metaclass=MyMetaClass):
def __init__(self, arg, macnab=None):
print(f'in Example.__init__({arg!r}, macnab={macnab!r})')
def setUp(self):
pass
def test_example1(self):
print("Test1")
def test_example2(self):
print("Test2")
def not_test(self):
print("Not a test")
def tearDown(self):
print("Also not a test")
pass
print('Creating instance of Example')
Example = Example(42, macnab='keyword')
Output:
Creating instance of Example
In metaclass generated __init__()
in Example.__init__(42, macnab='keyword')
In metaclass generated post_init()
calling Example.test_example1()
Test1
calling Example.test_example2()
Test2
For the first question, you can use dir() on self to get a list of its member (Ufficial Documentation for dir).
After that, you can test the name pattern in some simple way, and if it is callable you can call it:
for name in dir(self):
if name[:5] == 'test_' and callable(getattr(self, name)):
res = getattr(self, name)()
print(res)
Concerning you bonus question, it is a common practice to force the function to be overloaded.
I would imagine they're simply finding callable methods that begin with "test_" using the dir() function. Something you could achieve pretty easily like:
class CustomTestCaseRunner:
def run(self):
methods = [
m for m in dir(self)
if callable(getattr(self, m))
and m.startswith("test_")
]
for m in methods:
print(f"Running {self.__class__.__name__}.{m}")
getattr(self, m)()
class MyTest(CustomTestCaseRunner):
def test_foo(self):
assert True
def test_bar(self):
assert 1
MyTest().run()
# Running MyTest.test_bar
# Running MyTest.test_foo
As for your second question about assert True, it is unlikely you'd ever actually assert True in live code. That function appears to just be an example. assert is typically used on the response from a function. Here are a few examples:
assert isinstance(1, int)
assert isinstance("foo", str)
When the condition evaluates to False, it will raise an AssertionError which will fail your test case.
I want to create a class that behaves like collections.defaultdict, without having the usage code specify the factory. EG:
instead of
class Config(collections.defaultdict):
pass
this:
Config = functools.partial(collections.defaultdict, list)
This almost works, but
isinstance(Config(), Config)
fails. I am betting this clue means there are more devious problems deeper in also. So is there a way to actually achieve this?
I also tried:
class Config(Object):
__init__ = functools.partial(collections.defaultdict, list)
I don't think there's a standard method to do it, but if you need it often, you can just put together your own small function:
import functools
import collections
def partialclass(cls, *args, **kwds):
class NewCls(cls):
__init__ = functools.partialmethod(cls.__init__, *args, **kwds)
return NewCls
if __name__ == '__main__':
Config = partialclass(collections.defaultdict, list)
assert isinstance(Config(), Config)
I had a similar problem but also required instances of my partially applied class to be pickle-able. I thought I would share what I ended up with.
I adapted fjarri's answer by peeking at Python's own collections.namedtuple. The below function creates a named subclass that can be pickled.
from functools import partialmethod
import sys
def partialclass(name, cls, *args, **kwds):
new_cls = type(name, (cls,), {
'__init__': partialmethod(cls.__init__, *args, **kwds)
})
# The following is copied nearly ad verbatim from `namedtuple's` source.
"""
# For pickling to work, the __module__ variable needs to be set to the frame
# where the named tuple is created. Bypass this step in enviroments where
# sys._getframe is not defined (Jython for example) or sys._getframe is not
# defined for arguments greater than 0 (IronPython).
"""
try:
new_cls.__module__ = sys._getframe(1).f_globals.get('__name__', '__main__')
except (AttributeError, ValueError):
pass
return new_cls
At least in Python 3.8.5 it just works with functools.partial:
import functools
class Test:
def __init__(self, foo):
self.foo = foo
PartialClass = functools.partial(Test, 1)
instance = PartialClass()
instance.foo
If you actually need working explicit type checks via isinstance, you can simply create a not too trivial subclass:
class Config(collections.defaultdict):
def __init__(self): # no arguments here
# call the defaultdict init with the list factory
super(Config, self).__init__(list)
You'll have no-argument construction with the list factory and
isinstance(Config(), Config)
will work as well.
Could use *args and **kwargs:
class Foo:
def __init__(self, a, b):
self.a = a
self.b = b
def printy(self):
print("a:", self.a, ", b:", self.b)
class Bar(Foo):
def __init__(self, *args, **kwargs):
return super().__init__(*args, b=123, **kwargs)
if __name__=="__main__":
bar = Bar(1)
bar.printy() # Prints: "a: 1 , b: 123"
I'm trying to monkeypatch how pandas Panel's slicing (__getitem__). This is straightforward to do with a basic function, foo.
from pandas import Panel
Panel.__getitem__ = ORIGINAL_getitem
def newgetitem(panel, *args, **kwargs):
""" Append a string to return of panel.__getitem__"""
out = super(Panel, panel).__getitem__(*args, **kwargs)
return out+'custom stuff added'
Panel.__getitem__ = newgetitem
WhereORIGINAL_getitem is storing the original Panel method. I'm trying to extend to the case where foo() is not a function, but an instance method of an object, Foo. For example:
class Foo:
name = 'some name'
def newgetitem(self, panel, *args, **kwargs):
""" Append a string to return of panel.__getitem__,
but take attributes from self, like self.name
"""
out = super(Panel, panel).__getitem__(*args, **kwargs)
return out+'custom stuff added including name' + self.name
Foo.foo() must access the attribute self.name. Therefore, the monkeypatched function would need a reference to the Foo instance somehow, in addition to the Panel. How can I monkepatch panel with Foo.foo() and make self.name accessible?
The switching between the monkey patched function happens in another method, Foo.set_backend()
class Foo:
name = 'some name'
def foo(self):
return 'bar, called by %s' % self.name
def set_backend(self, backend):
""" Swap between new or original slicing."""
if backend != 'pandas':
Panel.__getitem__ = newgetitem
else:
Panel.__getitem__ = ORIGINAL_getitem
What I really need is for newgetitem to maintain a reference to self.
Solution Attempts
So far I've tried taking making newgetitem() a pure function, and using partial functions to pass a reference to self in. This doesn't work. Something like:
import functools
def newgetitem(foo_instance, panel, *args, **kwargs):
....
class Foo:
...
def set_backend(self, backend):
""" Swap between new or original slicing."""
if backend != 'pandas':
partialfcn = functools.partial(newgetitem, self)
Panel.__getitem__ = partialfcn
else:
Panel.__getitem__ = ORIGINAL_getitem
But this doesn't work. A reference to self is passed, but no access from the calling Panel possible. That is:
panel['50']
Passes a reference to Foo, not to Panel.
Yes, I know this is bad practice, but it's just a workaround for the time-being.
You can use patch from mock framework to handle your case. Even it is designed for testing, its primary work is monkey patching in defined contex.
Your set_backend() method could be:
def set_backend(self, backend):
if backend != 'pandas' and self._patched_get_item is None:
self._patched_get_item = patch("pandas.Panel.__getitem__", autospec=True, side_effect=self._getitem)
self._patched_get_item.start()
elif backend == 'pandas' and self._patched_get_item is not None:
self._patched_get_item.stop()
self._patched_get_item = None
That will work either when self._getitem is a method or a reference to a function.
One way to do this is to create a closure (a function with reference to names other than locals or globals). A simple closure:
def g(x):
def f():
"""f has no global or local reference to x, but can refer to the locals of the
context it was created in (also known as nonlocals)."""
return x
return f
func = g(1)
assert func() == 1
I don't have pandas on my system, but it works much the same with a dict.
class MyDict(dict):
pass
d = MyDict(a=1, b=2)
assert d['a'] == 1
class Foo:
name = 'name'
def create_getitem(fooself, cls):
def getitem(self, *args, **kwargs):
out = super(cls, self).__getitem__(*args, **kwargs)
return out, 'custom', fooself.name
# Above references fooself, a name that is not defined locally in the
# function, but as part of the scope the function was created in.
return getitem
MyDict.__getitem__ = Foo().create_getitem(MyDict)
assert d['a'] == (1, 'custom', Foo.name)
print(d['a'])
The basics of monkey patching are straightforward but it can quickly become tricky and subtle, especially if you're aiming at finding a solution that would work for both Python 2 and Python 3.
Furthermore, quickly hacked solutions are usually not very readable/maintenable, unless you manage to wrap the monkey patching logic nicely.
That's why I invite you to have a look at a library that I wrote especially for this purpose. It is named Gorilla and you can find it on GitHub.
In short, it provides a cool set of features, it has a wide range of unit tests, and it comes with a fancy doc that should cover everything you need to get started. Make sure to also check the FAQ!
I'm using the Python mock module for tests. I would like to replace an active object with a mock, and automatically have all calls made to the mock object forwarded to the original object. I think this is called a "Spy" in standard testing terminology. At the moment I'm doing inside a test:
# Insert a mock replacement
orig_active_attr = server.active_attr
server.active_attr = mock.Mock()
# Set up side effects to 'proxy' to the original object
server.active_attr.meth1.side_effect = orig_active_attr.meth1
server.active_attr.meth2.side_effect = orig_active_attr.meth2
# Call the method being tested
server.method_being_tested()
# Assert stuff on the mock.
server.active_attr.meth2.assert_called_once()
It would be nice if all method calls on the mock could be forwarded to the live object automatically without the boilerplate.
I seem to have stumbled across the solution:
import mock
class A(object):
def meth(self, a):
return a
a = A()
ma = mock.Mock(wraps=a)
Seems to work okay for functions, methods and properties, but not for class or instance attributes.
See the documentation.
You can use patch.object(wraps=obj_instance) as suggested in Spying on instance methods with Python's mock module.
For example:
from mock import patch
class Foo(object):
def bar(self, x, y):
return x + y + 1
def test_bar():
foo = Foo()
with patch.object(foo, 'bar', wraps=foo.bar) as wrapped_foo:
foo.bar(1, 2)
wrapped_foo.assert_called_with(1, 2)
Here's how to mock only datetime.date.today(), forwarding the rest of datetime calls to the datetime module:
from unittest import mock, TestCase
import foo_module
class FooTest(TestCase):
#mock.patch(f'{foo_module.__name__}.datetime', wraps=foo_module.datetime)
def test_something(self, mock_datetime):
# mock only datetime.date.today()
mock_datetime.date.today.return_value = datetime.date(2019, 3, 15)
# other calls to datetime functions will be forwarded to original datetime
foo_module imports datetime and uses many other datetime functions besides date.today.
You can use a simple function to iterate through all the method and configure your mock
def spy_mock(instance):
members = inspect.getmembers(instance, inspect.ismethod)
attrs = {'%s.side_effect' % k:v for k,v in members}
return mock.Mock(**attrs)
Usage would be
import inspect
from unittest import mock
class ActiveAttr:
def meth2(self):
print("Meth2 called")
class Server:
def __init__(self):
self.active_attr = ActiveAttr()
def method_being_tested(self):
self.active_attr.meth2()
def spy_mock(instance):
members = inspect.getmembers(instance, inspect.ismethod)
attrs = {'%s.side_effect' % k:v for k,v in members}
return mock.Mock(**attrs)
server = Server()
server.active_attr = spy_mock(server.active_attr)
server.method_being_tested()
server.active_attr.meth2.assert_called_once()
Extending upon the pattern from Wes McKinney (via Wilfred Hughes Answer), here is how to spy on a method/member of an object, where object is imported into module under test.
Note this solution is Python 2.x compliant!
module under test:
import object
def function_in_module_under_test():
object.method_from_imported_object()
testing spied assertion on method_from_imported_object:
from unittest import mock
import module_under_test
def test_method(self):
with mock.patch.object(
module_under_test.object,
"method_from_imported_object",
module_under_test.object.method_from_imported_object,
) as spy_method_from_imported_object:
# Demonstrate that subsequent spy asserts here, can be trusted
spy_method_from_imported_object.assert_not_called()
# now Test
module_under_test.function_in_module_under_test()
spy_method_from_imported_object.assert_called_once()
If you use pytest, the pytest-mock package has a convenient spy object.
class Foo(object):
def bar(self, v):
return v * 2
def test_spy_method(mocker):
foo = Foo()
spy = mocker.spy(foo, 'bar')
assert foo.bar(21) == 42
spy.assert_called_once_with(21)
assert spy.spy_return == 42
OK,
I know this is mentioned in the manual, and probably has to do with side_effect and/or return_value, but a simple, direct example will help me immensely.
I have:
class ClassToPatch():
def __init__(self, *args):
_do_some_init_stuff()
def some_func():
_do_stuff()
class UUT():
def __init__(self, *args)
resource_1 = ClassToPatch()
resource_2 = ClassToPatch()
Now, I want to unit test the UUT class, and mock the ClassToPatch. Knowing the UUT class will instantiate exactly two ClassToPatch objects, I want the Mock framework to return a new Mock object for each instantiation, so I can later assert calls on each separately.
How do I achieve this using the #patch decorator in a test case? Namely, how to fix the following code sample?
class TestCase1(unittest.TestCase):
#patch('classToPatch.ClassToPatch',autospec=True)
def test_1(self,mock1,mock2):
_assert_stuff()
Here's a quick'n'dirty example to get you going:
import mock
import unittest
class ClassToPatch():
def __init__(self, *args):
pass
def some_func(self):
return id(self)
class UUT():
def __init__(self, *args):
resource_1 = ClassToPatch()
resource_2 = ClassToPatch()
self.test_property = (resource_1.some_func(), resource_2.some_func())
class TestCase1(unittest.TestCase):
#mock.patch('__main__.ClassToPatch', autospec = True)
def test_1(self, mock1):
ctpMocks = [mock.Mock(), mock.Mock()]
ctpMocks[0].some_func.return_value = "funky"
ctpMocks[1].some_func.return_value = "monkey"
mock1.side_effect = ctpMocks
u = UUT()
self.assertEqual(u.test_property, ("funky", "monkey"))
if __name__ == '__main__':
unittest.main()
I've added test_property to UUT so that the unit test does something useful. Now, without the mock test_property should be a tuple containing the ids of the two ClassToPatch instances. But with the mock it should be the tuple: ("funky", "monkey").
I've used the side_effect property of the mock object so that a different instance of ClassToPatch is returned on each call in the UUT initialiser.
Hope this helps.
Edit: Oh, by the way, when I run the unit test I get:
.
----------------------------------------------------------------------
Ran 1 test in 0.004s
OK
Here is another version which is more generic to handle any number of instances created:
class TestUUT:
def test_init(self, mocker):
class MockedClassToPatchMeta(type):
static_instance = mocker.MagicMock(spec=ClassToPatch)
def __getattr__(cls, key):
return MockedClassToPatchMeta.static_instance.__getattr__(key)
class MockedClassToPatch(metaclass=MockedClassToPatchMeta):
original_cls = ClassToPatch
instances = []
def __new__(cls, *args, **kwargs):
MockedClassToPatch.instances.append(
mocker.MagicMock(spec=MockedClassToPatch.original_cls))
MockedClassToPatch.instances[-1].__class__ = MockedClassToPatch
return MockedClassToPatch.instances[-1]
mocker.patch(__name__ + '.ClassToPatch', new=MockedClassToPatch)
UUT()
# since your original code created two instances
assert 2 == len(MockedClassToPatch.instances)
If you need more thorough validation for each instance you can access MockedClassToPatch.instances[0] or MockedClassToPatch.instances[1].
I've also created a helper library to generate the meta class boilerplate for me. To generate the needed code for your example I wrote:
print(PytestMocker(mocked=ClassToPatch, name=__name__).mock_classes().mock_classes_static().generate())