I've got this production class:
class MyClass:
def __init__(self):
self.value = None
def set_value(self, value):
self.value = value
def foo(self):
# work with self.value here
# raise RuntimeError("error!")
return "a"
Which is being used from another place, like this:
class Caller:
def bar(self, smth):
obj = MyClass()
obj.set_value(smth)
# ...
# try:
obj.foo()
# except MyError:
# pass
obj.set_value("str2")
# obj.foo()
and I got this:
class MyError(Exception):
pass
In my test I want to make sure that Caller.bar calls obj.set_value, first with smth="a", then with smth="b", but I want it to really set the value (i.e. call the real set_value method). Is there any way for me to tell the mock to use the actual method, so I can later on read what it was called with?
P.S. I know that I can just change "foo" to require the parameter "smth" so I could get rid of "set_value", but I want to know if there is another option than this.
Okay, so I have tried this in my test:
def test_caller(self):
with patch('fullpath.to.MyClass', autospec=MyClass) as mock:
mock.foo.side_effect = [MyError("msg"), "text"]
caller = Caller()
caller.bar("str1")
calls = [call("str1"), call("str2")]
mock.set_value.assert_has_calls(calls)
But I see that the mock was not successful since the real "foo" is called when I wanted it to first raise MyError, then return "text".
Also, the assertion fails:
AssertionError: Calls not found.
Expected: [call('str1'), call('str2')]
Actual: []
The problem here is that you have mocked out your Class, and are not properly using the instance of your class. This is why things are not behaving as expected.
So, lets take a look at what is going on.
Right here:
with patch('fullpath.to.MyClass', autospec=MyClass) as mock:
So, what you are doing right here is mocking out your class MyClass only. So, when you are doing this:
mock.set_value.assert_has_calls(calls)
And inspect what is going on when you execute your unittest, your mock calls will actually contain this:
[call().set_value('str1'), call().foo(), call().set_value('str2')]
Pay attention to call as it is written as call(). call is with reference to your mock here. So, with that in mind, you need to use the called (aka return_value within context of the mocking world) mock to properly reference your mock object that you are trying to test with. The quick way to fix this is simply use mock(). So you would just need to change to this:
mock().set_value.assert_has_calls(calls)
However, to be more explicit on what you are doing, you can state that you are actually using the result of calling mock. Furthermore, it would actually be good to note to use a more explicit name, other than mock. Try MyClassMock, which in turn you name your instance my_class_mock_obj:
my_class_mock_obj = MyClassMock.return_value
So in your unit test it is more explicit that you are using a mocked object of your class. Also, it is always best to set up all your mocking before you make your method call, and for your foo.side_effect ensure that you are also using the instance mock object. Based on your recent update with your exception handling, keep your try/except without comments. Putting this all together, you have:
def test_caller(self):
with patch('tests.test_dummy.MyClass', autospec=MyClass) as MyClassMock:
my_class_mock_obj = MyClassMock.return_value
my_class_mock_obj.foo.side_effect = [MyError("msg"), "text"]
caller = Caller()
caller.bar("str1")
calls = [call("str1"), call("str2")]
my_class_mock_obj.set_value.assert_has_calls(calls)
Related
I've searched for hours. Can't find anyone even trying to do this. Hmmm.
I believe I have to override a single method within a class instance. I do not mean patch(return_value=). I need to make the method in question do something involving self.
I'll try to break it down. Liberally paraphrasing and including one of the many things I tried, which doesn't work ...
class SetupClass(object):
def set_some_stuff(self):
data_list = functon_cannot_be_run_on_test_platform()
self.something = data_list[0]
self.something_else = data_list[1]
class UUT(object):
self.config = SetupClass()
assert self.config.something == 'foo'
class UnitTests(TestCase):
#patch('SetupClass')
def test_UUT(self, mock1):
def WedgeClass(SetupClass):
def set_some_stuff(self):
self.something = 'foo'
pass # I'm a Python newbie, in too deep
wedge_class = WedgeClass()
mock1.return_value = wedge_class # doesn't work. context errors
uut = UUT() # <-- would crash here, because assert above
Assume that I cannot make changes to UUT or SetupClass.
Testing cannot even get off the ground because the assertion will fail, due to, SetupClass.functon_cannot_be_run_on_test_platform(). Note that simply mocking SetupClass.functon_cannot_be_run_on_test_platform will not solve the problem, because reasons.
ATM, I figure the only way to get around this mess is to somehow override SetupClass.set_some_stuff. I cannot simply mock the entire class, because UUT relies heavily on its other functionality as well. I need everything to work as is, except this one method and I need that method to be able to access, self in the same context as originally intend.
I tried various things involving subclassing and mock.return_value etc. I'd rather not recall the pain that caused. :p
My kingdom for test-driven code in the first place! This code contains a convolution of co-dependencies. :-/
Apart from the multiple errors in the example code I wrote up off the top of my head at the cafe ...
The problem was (mostly) that I was using return_value instead of side_effect to 'replace' the patched class with my own subclass.
My need, re-stated perhaps more clearly now, is to override a single method, set_some_stuff, in a class within the unit under test (UUT) but without mocking the class.
The method issues several 'self.foo = bar' statements, which I want to change for testing purposes. Thus, mocking the method's (unused) return value is not enough ... and patch.object seems to lose class context, "'self' unknown" or the like.
Finally, here is the working code, doing what I need ...
import unittest
import mock
class SetupClass(object):
def set_some_stuff(self):
data_list = ['data not available', 'on test_platform'] # CRASH!
self.something = data_list[0]
self.something_else = data_list[1]
class UUT:
def __init__(self):
self.config = SetupClass()
self.config.set_some_stuff()
assert self.config.something == 'foo' # <-- used to crash before here ...
self.got_here = True # ... but now the override method from
# WedgeClass is being used! :=)
"""
Subclass the original class, overriding just the method in question. Then set
the subclass as the side_effect of the patched original, effectively replacing it.
"""
class WedgeClass(SetupClass):
def set_some_stuff(self):
self.something = 'foo'
pass # I'm a Python newbie, in too deep
class UnitTests(unittest.TestCase):
#mock.patch(__module__+'.SetupClass')
def test_UUT(self, mock1):
wedge_class = WedgeClass()
mock1.side_effect = WedgeClass # <--- Ureka! 'side_effect' not 'return_value' was the ley!
uut = UUT()
self.assertTrue(uut.got_here)
I must be tired, because surely there is an easy way to do this.
But I've read over the pytest docs and can't figure out this simple use case.
I have a little package I want to test:
class MyClass:
def __init__(self):
pass
def my_method(self, arg):
pass
def the_main_method():
m = MyClass()
m.my_method(123)
I would like to ensure that (1) an instance of MyClass is created, and that (2) my_method is called, with the proper arguments.
So here's my test:
from unittest.mock import patch
#patch('mypkg.MyClass', autospec=True)
def test_all(mocked_class):
# Call the real production code, with the class mocked.
import mypkg
mypkg.the_main_method()
# Ensure an instance of MyClass was created.
mocked_class.assert_called_once_with()
# But how do I ensure that "my_method" was called?
# I want something like mocked_class.get_returned_values() ...
I understand that each time the production code calls MyClass() the unittest framework whips up a new mocked instance.
But how do I get my hands on those instances?
I want to write something like:
the_instance.assert_called_once_with(123)
But where do I get the_instance from?
Well, to my surprise, there is only one mock instance created, no matter how many times you call the constructor (:
What I can write is:
mocked_class.return_value.my_method.assert_called_once_with(123)
The return_value does not represent one return value, though — it accumulates information for all created instances.
It's a rather abstruse approach, in my mind. I assume it was copied from some crazy Java mocking library (:
If you want to capture individual returned objects, you can use .side_effect to return whatever you want, and record it in your own list, etc.
From here, if you define some objects like that:
class Mixin1(object):
def test(self):
print "Mixin1"
class Mixin2(object):
def test(self):
print "Mixin2"
class BaseClass(object):
pass
class MyClass(Mixin2, Mixin1, BaseClass):
pass
You'll get:
>>> obj = MyClass()
>>> obj.test()
Mixin2
Is there a way to call Mixin1 test() method?
Call it explicitly:
Mixin1.test(obj)
The attribute process in Python is relatively complex. For your given example, this is the process for finding the value of obj.test:
First, look at the instance itself. In this case, the instance does not have a test attribute.
Look at the class which obj is an instance of: MyClass. MyClass does not have a test attribute.
Start looking at the classes in the method resolution order of MyClass. In this case, MyClass.__mro__ tells you to look first at Mixin2, then Mixin1, then object.
Mixin2 has a test attribute, so we finally have a match.
Mixin2.test is a function with a __get__ method, so that is called and the return value is used.
You can safely ignore step 5 here, and just assume that Mixin2.test is a method. One that is returned, you can see that obj.test() calls Mixin2.test.
This might help explain why I asked the question I did in a comment. There is a wide variety of ways you can fiddle with the program to get obj.test() to produce a call to Mixin1.test() instead. You can patch the object, you can fiddle with MyClass.__mro__, you can tweak what Mixin2.test actually does, etc.
Override the test method and call Mixin1.test explicitly:
class MyClass(Mixin2, Mixin1, BaseClass):
def test(self):
Mixin1.test(self)
My class looks like this:
class A:
def __init__(self):
self.bar = []
...
#property
def foo(self):
return bar
Is there a way to find out inside foo whether a method will be called on its return value? I would like to be able to change the return value of foo depending on whether
a.foo.foobar()
or
a.foo
is called.
You could use a proxy class wrapping self.bar (or just self FWIW) in foo()) and overload the proxy's __getattr__() or __getattribute__ methods (more tricky and can slow down your program quite a bit but well...).
Now the question is: what is your real problem ? There might be better / safer solutions...
for the fun of it...
#!/usr/bin/python
import traceback
def how_was_i_called():
call=traceback.extract_stack(limit=2)[0][3]
print "I was called like this: %s"%call
how_was_i_called()
try:
how_was_i_called().foobar()
except AttributeError:
pass
returns:
I was called like this: how_was_i_called()
I was called like this: how_was_i_called().foobar()
but please do not use hacks like this in real applications...
No, there is not. foo returns, and what happens with the return value after that is an entirely separate issue.
You could do this, for example:
result = a.foo
if some_condition:
result.foobar()
e.g. accessing the foobar method on a.foo is an entirely separate expression that may or may not be executed. This could happen at a much later time too, or in a separate thread, or after serialising the object to disk, then loading it again, etc.
You can hook into attribute access on the returned object, but that'll be too late for your foo property to alter behaviour.
I want to test a method but mock out other methods that it calls. I created this simple example that should illustrate the concept:
class myClass():
def one_method(self):
print "hey"
def two_deep(self):
self.one_method()
def three_deep(self):
self.two_deep()
I was using a python mock framework called Mox and wrote the following code to do this:
def test_partial(self):
self_mox = mox.Mox()
some_object = myClass()
## 1. make your mock
my_mock = mox.MockObject(some_object)
my_mock.one_method().AndReturn('some_value')
self_mox.ReplayAll()
ret = my_mock.three_deep() ## *** SEE NOTE BELOW called "comment":
self_mox.VerifyAll()
Comment:
I thought that if I called this mock on a method that hadn't been overwritten, then the mock would default to the original code, then I could get the chain of calls that I want, with the last call being replaced... but it doesn't do this. I can't figure out how to embed a mock object inside a test object that doesn't have an inserting method.
I looked into Partial Mocks and Chained Mocks to solve this, but I couldn't find a way to pull this off.
Thanks for any help :)
-- Peter
Check documentation for StubOutWithMock:
https://code.google.com/p/pymox/wiki/MoxDocumentation#Stub_Out
https://code.google.com/p/pymox/wiki/MoxRecipes#Mock_a_method_in_the_class_under_test.
So what you needed is:
def test_partial(self):
self_mox = mox.Mox()
# Create the class as is, instead of doing mock.
some_object = myClass()
# Stub your particular method using the StubOutWithMock.
m.StubOutWithMock(some_object, "one_method")
some_object.one_method().AndReturn('some_value')
self_mox.ReplayAll()
ret = some_object.three_deep()
self_mox.UnsetStubs()
self_mox.VerifyAll()