Destroy a mock in Python after the test - python

Let's say I have a couple of tests like these:
class TestMyTest(unittest.TestCase):
def SetUpClass(cls):
cls.my_lib = MyLib()
def my_first_test(self):
self.my_lib.my_function = Mock(return_value=True)
self.assertTrue(self.my_lib.run_my_function(), 'my function failed')
def my_second_test(self):
# Some other test that calls self.my_lib.my_function...
And let's say I have something like this in MyLib:
class MyLib(Object):
def my_function(self):
# This function does a whole bunch of stuff using an external API
# ...
def run_my_function(self):
result = self.my_function()
# Does some more stuff
# ...
In my_first_test I am mocking my_lib.my_function and returning a True when the function is executed. In this example, my assertion is calling run_my_function(), which is another function from the same library that among other things, it calls my_lib.my_function. But when my_second_test is executed I don't want the mocked function to be called but the real one. So I guess I would need to destroy the mock somehow after running my_first_test, maybe during tearDown(). How do I destroy that mock?
I edited my original question to add more details since looks like it was not that clear, sorry about that.

You can do this:
class TestYourLib(unittest.TestCase):
def setUp(self):
self.my_lib = MyLib()
def test_my_first_test(self):
self.my_lib.my_function = Mock(return_value=True)
self.assertTrue(self.run_my_function(), 'my function failed')
def test_my_second_test(self):
# Some other test that calls self.my_lib.my_function...
Then the Mock is "destroyed" by passing out of scope when setUp is called for the next test case.

Destroying the mock won't do it. You'll either have to re-assign self.my_lib.my_function or call Mock(return_value=True) in a different manner.
The first is what Patrick seems to suggest.

Related

Calling functions from different Classes in Python

I'm relatively new in python and I have the following question, my code looks something like this:
class Hkprop:
def hkprop_f(self):
hkprop= self.Mkprop().fun2() + self.Fkprop().fun4()
return hkprop
class Fkprop:
def fun1(self):
#something
def fun2(self):
self.fun1()
class Mkprop:
def fun3(self):
#something
def fun4(self):
self.fun1() #here I want to call fun1 but I don't know if it is possible or how to do it
I know this might be a super basic question, but if someone could help me I would really appreaciate it. I've been looking and came across #staticmethod, but I didn't completely understand. Is there a way of calling the function without using it?
If you want to call fun1, you can do Hkprop.Fkprop.fun1()
Yes, this is basic but sometimes, even such basic questions takes a little time to figure it out. So, I am assuming that your indentations are correct, and you have nested classes. Here is the solution that I did.... Hope this is helpful to you...
You will need to use the class inheritance methodolody, or simply learn how to instantiate class objects from one class to another as shown below.
When you save this code to a file a run it. You will see that first it will instantiate Hkprop.Mkprop and the call fun4. The next thing, we have done is instantiate Hkprop.Fkprop in func4 to be able to call func1 in FKprop class.
class Hkprop:
def hkprop_f(self):
hkprop= self.Mkprop().fun2() + self.Fkprop().fun4()
return hkprop
class Fkprop:
def fun1(self):
print('I just called this function')
def fun2(self):
self.fun1()
class Mkprop:
def fun3(self):
print('this is soemthing')
def fun4(self):
fk = Hkprop.Fkprop()
fk.fun1()
if __name__ == '__main__':
mk = Hkprop.Mkprop()
mk.fun4()
Let's breakdown your code. You wrote:
def fun4(self):
self.fun1()
There are 2 problems here.
self represents the instance of the class. Since, Mkprop class has no defintion of fun1() so it cannot access fun1()
fun1() function also expects a parameter in Fkprop class
In fun4, if you want to call fun1() then make the following change:
def fun4(self):
Hkprop.Fkprop.fun1(self)
Moreover, in python, functions cannot have empty body. Your current code will get the error:
IndentationError: expected an indented block
Because the interpreter expects a function body that is indented. To keep the empty functions, change fun1 & fun3 functions like this
def fun1():
pass
def fun3():
pass
pass is a special statement in Python that does nothing. It only works as a dummy statement.
I never tried nested classes before. In your case, it seems like you're calling a nonexistent function from a class. fun1() does not exist in class Mkprop.
If you want to call fun1(), you have to call it from it's respective class, therefore, Hkprop().Fkprop().fun1(). It goes something like this: Parent Class -> Sub class -> Function.

How to use mock in function run in multiprocessing.Pool

In my code, I use multiprocessing.Pool to run some code concurrently. Simplified code looks somewhat like this:
class Wrapper():
session: Session
def __init__(self):
self.session = requests.Session()
# Session initialization
def upload_documents(docs):
with Pool(4) as pool:
upload_file = partial(self.upload_document)
pool.starmap(upload_file, documents)
summary = create_summary(documents)
self.upload_document(summary)
def upload_document(doc):
self.post(doc)
def post(data):
self.session.post(self.url, data, other_params)
So basically sending documents via HTTP is parallelized. Now I want to test this code, and can't do it. This is my test:
#patch.object(Session, 'post')
def test_study_upload(self, post_mock):
response_mock = Mock()
post_mock.return_value = response_mock
response_mock.ok = True
with Wrapper() as wrapper:
wrapper.upload_documents(documents)
mc = post_mock.mock_calls
And in debug I can check the mock calls. There is one that looks valid, and it's the one uploading the summary, and a bunch of calls like call.json(), call.__len__(), call.__str__() etc.
There are no calls uploading documents. When I set breakpoint in upload_document method, I can see it is called once for each document, it works as expected. However, I can't test it, because I can't verify this behavior by mock. I assume it's because there are many processes calling on the same mock, but still - how can I solve this?
I use Python 3.6
The approach I would take here is to keep your test as granular as possible and mock out other calls. In this case you'd want to mock your Pool object and verify that it's calling what you're expecting, not actually rely on it to spin up child processes during your test. Here's what I'm thinking:
#patch('yourmodule.Pool')
def test_study_upload(self, mock_pool_init):
mock_pool_instance = mock_pool_init.return_value.__enter__.return_value
with Wrapper() as wrapper:
wrapper.upload_documents(documents)
# To get the upload file arg here, you'll need to either mock the partial call here,
# or actually call it and get the return value
mock_pool_instance.starmap.assert_called_once_with_args(upload_file, documents)
Then you'd want to take your existing logic and test your upload_document function separately:
#patch.object(Session, 'post')
def test_upload_file(self, post_mock):
response_mock = Mock()
post_mock.return_value = response_mock
response_mock.ok = True
with Wrapper() as wrapper:
wrapper.upload_document(document)
mc = post_mock.mock_calls
This gives you coverage both on your function that's creating and controlling your pool, and the function that's being called by the pool instance. Caveat this with I didn't test this, but am leaving some of it for you to fill in the blanks since it looks like it's an abbreviated version of the actual module in your original question.
EDIT:
Try this:
def test_study_upload(self):
def call_direct(func_var, documents):
return func_var(documents)
with patch('yourmodule.Pool.starmap', new=call_direct)
with Wrapper() as wrapper:
wrapper.upload_documents(documents)
This is patching out the starmap call so that it calls the function you pass in directly. It circumvents the Pool entirely; the bottom line being that you can't really dive into those subprocesses created by multiprocessing.

Unittest and mocks, how to reset them?

I am testing a class that needs a mock in the constructor, so I usually do this:
class TestActionManager(unittest.TestCase):
#patch('actionlib.SimpleActionClient', return_value=create_autospec(actionlib.SimpleActionClient))
def setUp(self, mock1):
self.action_manager = ActionManager()
Then in this class I add all the tests. So the first one is working fine
def test_1(self):
self.action_manager.f()
self.action_manager.f.assert_called_once()
But if I add another test and run both
def test_2(self):
self.action_manager.f()
self.action_manager.f.assert_called_once()
It says f has been called twice. I was expecting setUp to create a new ActionManager (and hence create a new mock) before starting every test, but it is clearly not happening, since the mock is somehow shared. Also I tried to do
def tearDown(self):
del self.action_manager
But it does not fix the problem.
I have read something related in
Python Testing - Reset all mocks?
where the solution is to use a different library (something that I would like to avoid)
and in Any way to reset a mocked method to its original state? - Python Mock - mock 1.0b1 where it is using different classes to do it.
Is there any possibility to reset the mock in the same class before or after every test?
BTW, this is a unittest question, not a pytest question.
Anyways,
I believe what you're looking for is reset_mock
Here's, in general, how it works:
def test_1(self):
f = MagicMock() # or whatever you're mocking
f()
f.assert_called_once()
f.reset_mock()
f()
f.assert_called_once()
The result will be PASSED
If you want to automate, then you store the mocked thing inside setUp, and in tearDown you call the mocked thing's .reset_mock() method.
def setUp(self, mock1):
self.mock1 = mock1
# ... do other things ...
def tearDown(self):
self.mock1.reset_mock()

Mocking both Model.objects.bulk_create and Model.save()

I'm attempting to mock both bulk_create and save for a dry run.
I've created code which in essence does following:
#mock.patch.object(SomeModel.objects, 'bulk_create')
#mock.patch.object(SomeModel, 'save')
def a_dry_run(mocked_bulk_create, mocked_save):
def print_bulk(obj_list):
print 'doing bulk_create'
for obj in obj_list:
print obj
def print_save(instance):
print 'doing save'
print instance
mocked_bulk_create.side_effect = print_bulk
mocked_bulk_create.return_value = True
mocked_save.side_effect = print_save
mocked_save.return_value = True
The problem is that when I do bulk_create instead of print_bulk it executes print_save. How do I fix that or is there a cleaner way to do that?
The order of argument should be reversed. As you can see at Quick Guide where describe patch the order of mock arguments passed to the function follow patch's decorator from bottom to top.
In you case simply change the patch order or the function signature like
def a_dry_run(mocked_save, mocked_bulk_create):

Python mock: mock object not updated when running multiple tests

I'm trying to mock a function calling a remote_api:
def get_remote_value():
ret = make_distant_call()
return ret > 0
This function is called in another function:
from another_file import get_remote_value
def check_remote_value():
remote_value = get_remote_value()
# Actually do some computation but it doesn't change the issue
return remote_value
Here's my test:
#mock.patch('another_file.get_remote_value')
class MyTest(TestCase):
def first_test(self, mock_get_remote_value):
mock_get_remote_value.return_value = True
self.assertEqual(check_remote_value(), True)
def second_test(self, mock_get_remote_value):
mock_get_remote_value.return_value = False
self.assertEqual(check_remote_value(), False)
When I run each test on its own, it works fine. When I run the whole class, the second test fails because get_remote_value returns True and not False.
I'm thinking the check_remote_value function is still using the old mock and that's what's causing the issue. Am I right? In anyway, how can I change my test so that it runs smoothly?
I tried using the decorator on each function, using the patch context manager, to no avail. Mocking the whole check_remote_value is not really an option since it's the one I want to test.
You need to patch the name that check_remote_value actually uses.
#mock.path('mymodule.utils.another_file.get_remote_value')
class MyTest(TestCase):
def first_test(self, mock_get_remote_value):
mock_get_remote_value.return_value = True
self.assertEqual(check_remote_value(), True)
def second_test(self, mock_get_remote_value):
mock_get_remote_value.return_value = False
self.assertEqual(check_remote_value(), False)
This is due to how functions look up global values. check_remote_value has a reference to the global scope defined in mymodule.utils, not your test script, so that is where it looks when it needs to look up get_remote_value.

Categories