pkiller162
pkiller162

Reputation: 422

Can I define and setup a context manager before using with

I am using requests to create a requests.Session and set it up inside a function:

def create_web_session(cookie=None):
    s = requests.Session()
    if cookie is not None:
        s.cookies.set("my_cookie_name", cookie)
    return s

Sessions can be used as context managers. Can I use the function that returns the session (above) in a with statement?

with create_web_session("my_cookie_value") as s:
     s.get(...)

Or would I have to change the function that instead takes a session and sets it up inside the context manager:

with requests.Session() as s:
    setup_web_session(s, "my_cookie_value")
    s.get(...)

Running the former seemed to work however my unit tests failed which is why I asked this question. I am patching the create_web_session and upon testing the s.get(...) I am asserting that my mock gets called with .get() however it seems to be session_mock.__enter__() that is calling it. Is this expected behavior?

Here is an example:

# Function
def my_function():
    s = create_web_session()
    s.get("https://google.com")
    s.close()

# Test
@patch("foo.bar.create_web_session")
def test_my_function(self, mock_create_web_session):
    my_function()
    mock_create_web_session.assert_called_once()
    mock_create_web_session.return_value.get.assert_called_once()
    mock_create_web_session.return_value.close.assert_called_once()

Once I change the function to use context managers:

def my_function():
    with create_web_session() as s:
        s.get("https://google.com")

The test fails with: Expected 'get' to have been called once. Called 0 times.

Upvotes: 0

Views: 197

Answers (1)

Alex Hall
Alex Hall

Reputation: 36043

Your create_web_session is fine. The problem in the test is that while requests.Session.__enter__ simply returns back the same session, all methods on a mock return a fresh mock object. We can tell the mock to behave how we want and get a working test case like so:

def test_my_function(self, mock_create_web_session):
    session = mock_create_web_session.return_value
    session.__enter__.return_value = session

    my_function()

    mock_create_web_session.assert_called_once()
    session.get.assert_called_once()
    session.__exit__.assert_called_once()

Note that I assert __exit__ is called, not close, because the mock doesn't know anything about close or real sessions.

Upvotes: 1

Related Questions