Python mock: mock object not updated when running multiple tests - python

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.

Related

How to find if this is the first call or second call for a fixture in pytest

I'm trying to figure out if there are any ways that when I use a fixture and if this is the first time it returns true otherwise returns false.
Let's say:
#pytest.fixture( scope = "session" )
def first() :
# first call should return true
The scope I want to test is session and I know this fixture only runs one time per session but I want to modify this behaviour in any way.
Since the scope you use is session, the code in def first() would run once and produce a single object. However, this returned object can be anything you want and behave in any way you want.
For example, you can return an endless generator which would return True the first time and False for any additional calls of next, like so:
import itertools
import pytest
#pytest.fixture(scope="session")
def first():
# first call should return true
return itertools.chain(itertools.repeat(True, 1), itertools.repeat(False))
def test_one(first):
assert next(first)
def test_two(first):
assert not next(first)
def test_three(first):
assert not next(first)
This is how you can distinguish in your tests whether this is the first time.

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()

Why does python mock.patch work differently when second patch argument is used vs return_value?

I'm trying to mock out the return value of a dependent method but the return value is different between using the return_value and adding an additional argument to the mock.patch. Please help me figure out why. I tried searching online but couldn't find an answer to this.
library/abc.py:
from tools.operating_system import os_name
class ABC(object):
def get_os_info(self):
return os_name()
tools/operating_system.py:
import os
def os_name():
return os.name
library/test_abc.py:
from unittest import TestCase, mock
from library.abc import ABC
class TestMain(TestCase):
# This works because the name method returns `test`
def test_mocking_os_name(self):
with mock.patch('tools.operating_system.os.name', 'test'):
abc = ABC()
res = abc.get_os_info()
self.assertEqual(res, 'test')
# The test fails because the name method returns `<MagicMock name='name' id='4515046400'>`
def test_mocking_os_name(self):
with mock.patch('tools.operating_system.os.name') as mock_name:
mock_name.return_value = 'test'
abc = ABC()
res = abc.get_os_info()
self.assertEqual(res, 'test')
Note: This example is somewhat contrived because I could mock the os_name method. That is not the main concern. I'm trying to learn python test mocking and I want to be able to mock os.name. Thanks! This is for Python 3.7.2 but I have the same problem with Python 2.7.15
See the documentation for mock.patch: when you call this
with mock.patch('tools.operating_system.os.name', 'test')
you're doing this:
with mock.patch('tools.operating_system.os.name', NEW='test')
You're replacing os.name with the string test, so that works as you'd expect.
When you call this
with mock.patch('tools.operating_system.os.name') as mock_name:
you're doing this:
with mock.patch('tools.operating_system.os.name', NEW=DEFAULT) as mock_name:
You're leaving NEW as NEW=DEFAULT so os.name is set to MagicMock object (as the documentation says). Setting mock_name.return_value='test' does not work, because you're not calling the function os.name() anywhere in your code, and of course you should not be, because os.name is a string and not a function.
Like you said, in real code you would want to be patching os_name function instead of os.name:
def test_patching_os_name(self):
with mock.patch('library.abc.os_name') as mock_os_name:
mock_os_name.return_value = 'test'
abc = ABC()
res = abc.get_os_info()
self.assertEqual(res, 'test')
This test is not depending on the internal implementation of os_name function. If the correct behaviour of ABC class depended on os_name always returning os.name (which is what the tests are asserting), then the class could just as well use os.name itself and not depend on tools.operating_system at all.
A little personal preference for the end: I prefer injecting dependencies instead of patching whenever possible, see, for example, the article "Every mock.patch() is a little smell".

Can Pytest only instantiates one class object to test all its methods?

I think this should be a very common scenario, for example, if you have a class and a few methods for it. If you want to write unittest for them, like test_method_1, test_method_2, ... test_method_n, I do not want to instantiate the class object for each of these test functions, it could be redundant or inefficient. However, I read the doc for Pytest, it looks to me for example by using fixture decoration, although it appears to write class instantiation only once, actually this instantiation would be called every time when it passes to a new test function. Is there a way not to do this but instead I just create the class object only once, and all tests are done within this object ?
Fixtures can have different scopes, or, in other words, be called once for every function that uses it, or once for every test module that uses it, or once for every test session that uses it. See https://docs.pytest.org/en/latest/fixture.html#scope-sharing-a-fixture-instance-across-tests-in-a-class-module-or-session
This sample script will fail if you use the default scope (function) and pass if you use another scope.
import pytest
SCOPE="function"
#SCOPE="session"
class Shared(object):
counter = 0
def __init__(self):
self.instance_id = Shared.counter
Shared.counter += 1
#pytest.fixture(scope=SCOPE)
def shared_instance():
instance = Shared()
yield instance
def test_one(shared_instance):
assert shared_instance.instance_id == 0
def test_two(shared_instance):
assert shared_instance.instance_id == 0

Destroy a mock in Python after the test

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.

Categories