I have situation similar to:
class BaseClient(object):
def __init__(self, api_key):
self.api_key = api_key
# Doing some staff.
class ConcreteClient(BaseClient):
def get_some_basic_data(self):
# Doing something.
def calculate(self):
# some staff here
self.get_some_basic_data(param)
# some calculations
Then I want to test calculate function using mocking of get_some_basic_data function.
I'm doing something like this:
import unittest
from my_module import ConcreteClient
def my_fake_data(param):
return [{"key1": "val1"}, {"key2": "val2"}]
class ConcreteClientTest(unittest.TestCase):
def setUp(self):
self.client = Mock(ConcreteClient)
def test_calculate(self):
patch.object(ConcreteClient, 'get_some_basic_data',
return_value=my_fake_data).start()
result = self.client.calculate(42)
But it doesn't work as I expect.. As I thought, self.get_some_basic_data(param) returns my list from my_fake_data function, but it looks like it's still an Mock object, which is not expected for me.
What is wrong here?
There are two main problems that you are facing here. The primary issue that is raising the current problem you are experiencing is because of how you are actually mocking. Now, since you are actually patching the object for ConcreteClient, you want to make sure that you are still using the real ConcreteClient but mocking the attributes of the instance that you want to mock when testing. You can actually see this illustration in the documentation. Unfortunately there is no explicit anchor for the exact line, but if you follow this link:
https://docs.python.org/3/library/unittest.mock-examples.html
The section that states:
Where you use patch() to create a mock for you, you can get a
reference to the mock using the “as” form of the with statement:
The code in reference is:
class ProductionClass:
def method(self):
pass
with patch.object(ProductionClass, 'method') as mock_method:
mock_method.return_value = None
real = ProductionClass()
real.method(1, 2, 3)
mock_method.assert_called_with(1, 2, 3)
The critical item to notice here is how the everything is being called. Notice that the real instance of the class is created. In your example, when you are doing this:
self.client = Mock(ConcreteClient)
You are creating a Mock object that is specced on ConcreteClient. So, ultimately this is just a Mock object that holds the attributes for your ConcreteClient. You will not actually be holding the real instance of ConcreteClient.
To solve this problem. simply create a real instance after you patch your object. Also, to make your life easier so you don't have to manually start/stop your patch.object, use the context manager, it will save you a lot of hassle.
Finally, your second problem, is your return_value. Your return_value is actually returning the uncalled my_fake_data function. You actually want the data itself, so it needs to be the return of that function. You could just put the data itself as your return_value.
With these two corrections in mind, your test should now just look like this:
class ConcreteClientTest(unittest.TestCase):
def test_calculate(self):
with patch.object(ConcreteClient, 'get_some_basic_data',
return_value=[{"key1": "val1"}, {"key2": "val2"}]):
concrete_client = ConcreteClient(Mock())
result = concrete_client.calculate()
self.assertEqual(
result,
[{"key1": "val1"}, {"key2": "val2"}]
)
I took the liberty of actually returning the result of get_some_basic_data in calculate just to have something to compare to. I'm not sure what your real code looks like. But, ultimately, the structure of your test in how you should be doing this, is illustrated above.
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.
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)
I am new to unit testing and mocking in python, in below scenario I know how to mock the get_name() which doesn't have any parameter mock example, but I am not able to mock the below scenario which take age as an argument. Could you please help me to fix mock test_name function below?
# data_source.py
def get_name(a):
return "Alice"+str(a)
def get_age():
return 30
The Person class exposes a method that fetches data from the data source:
# person.py
from data_source import get_name
class Person(object):
def name(self):
age = get_age()
return get_name(age)
from mock import patch
from person import Person
#patch('person.get_name')
def test_name(mock_get_name):
mock_get_name.return_value = "Bob"
person = Person()
name = person.name()
assert name == "Bob"
Thanks in advance!
First of all, apart from importing get_name, you also need to import get_age, since you use it in the Person class. Second, there is nothing like person.get_name defined ever. So you can't patch that. Instead, you probably wanted to patch data_souce.get_name.
But unfortunately, that doesn't work, because data_source is a python module, and get_name is a method within it that you want to redefine. The patch decorator would be good if get_name were a method within a DataSource class, for example. I don't know the exact reason why it doesn't work, because I did not take a deep dive into the implemenation details of the mock library, but the intention of the library is different than how you want to use it.
To solve the problem, you can eiter turn data_source into a class, or you can temporarily change the meaning of the name get_name, by assigning a different function or a lambda to it:
import data_source
def test_person():
# replace the function
data_source._real_get_name = data_source.get_name
data_source.get_name=lamba a: "Bob"
# do some work
person = Person()
name = person.name()
# after use of the get_name function, put the original function back
data_source.get_name=data_source._real_get_name
# assert post condition
assert name=="Bob"
Of course, changing what functions do severely affects your program and should be done with care, probably only for testing. Make sure you always put the original function back, even if an error is thrown unexpectedly.
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()