Python: Mocking a context manager
You are setting the wrong mock: mock_tmp
is not the context manager, but instead returns a context manager. Replace your setup line with:
mock_tmp.return_value.__enter__.return_value.name = mytmpname
and your test will work.
Python patch context manager to return object
The following should work:
_mocked_ctx_manager.return_value.__enter__.return_value = client
Your _mocked_ctx_manager
returns a context manager. Therefore you need to set the __enter__.return_value
of _mocked_ctx_manager.return_value
.
I found the following article to be helpful: Surrender Python Mocking! I Have You Now.
How do you use patch() as a context manager?
I have figured out the issue. When patch is given a class, it will return a class, not an object of that class. So mock in my example is not a DataBaseMock object, but a reference to the class. This is why class level variables are visible, but not object fields. In order get my desired functionality, I did this:
class TestFunctions():
def test_function(self):
with patch("path.to.database.call") as mock:
mock.return_value = DataBaseMock()
result = function_i_am_testing()
assert mock.return_value.x == result
Now, mock is a MagicMock object, whose return value is the object I need.
Mock requests.Session.get in a nested context manager
You got it almost right, the correct way to mock is:
with patch("main.Session") as mocked_session:
mocked_session.return_value.__enter__.return_value.get.return_value.__enter__.return_value = "MOCKED"
I will break this down for clarification.
Your session_mock
refers to the Session
class. To get the Session
instance, you use return_value
(as you do for every call).
The with
statement internally calls __enter__()
on that session to get the session
instance, so you have to add __enter__
for the method and return_value
for the call.
Now you call get()
on that object, which by the same logic needs an added get.return_value
.
And finally, this is used in another with
statement to create response
, which again needs an added __enter__.return_value
.
The result gets you the response
object for your mocked Session
, and you can set another value.
If breaking that down in code, it would look something like:
with patch("main.Session") as mocked_session:
mocked_session_instance = mocked_session.return_value
mocked_session_object = mocked_session_instance.__enter__.return_value
mocked_get = mocked_session_object.get.return_value
mocked_response = mocked_get.__enter__
mocked_response.return_value = "MOCKED"
Python - create mock test for class method that has context manager
Thanks to the commenters I have found a solution that works for me. The trick was to patch the correct class, in this case I wanted to patch db_class.db.a instead of db_class.db. After that, it is important to make sure that the fetch() call is a method (I think I'm getting that correct). The tricky part about this problem for me was patching the correct thing as well as dealing with the context manager which requires a bit of extra tinkering.
@patch('db_class.db.a')
def test(db_a):
expected_result = [5,10]
b_fetch = MagicMock()
b_fetch.fetch.return_value = expected_result
db_a.return_value = Mock(b = b_fetch,
__enter__= db_a,
__exit__ =Mock())
foo = Foo()
result = foo.method()
assert result == expected_result
if __name__ == "__main__":
test()
Related Topics
How to Write Tests for the Argparse Portion of a Python Module
Print Statement Inside of Input Returns with a "None"
Appending Item to Lists Within a List Comprehension
Sphinx's Autodoc's Automodule Having Apparently No Effect
Error Running Basic Tensorflow Example
Getting Values from Object Oriented Tkinter
Truncate to Three Decimals in Python
Why Can't Environmental Variables Set in Python Persist
Python Module with a Dash, or Hyphen (-) in Its Name
Pandas Select Rows and Columns Based on Boolean Condition
How to Make an Image with a Transparent Backround in Pygame
Using Print() (The Function Version) in Python2.X
How to Set the Default Color Cycle for All Subplots with Matplotlib