I am attempting to unit test some methods called by a flask application. The methods interact with a flask Response object, which I am trying to mock up for testing purposes (I don't want to test the flask Response, it works just fine.) When I try to run my tests, the tests run as expected but they throw an exception during teardown.
Example code
def my_func():
key = do_some_stuff()
if key:
return Response("key", status=200)
else:
return Response("key", status=500)
Fixture code from conftest.py
@pytest.fixture()
def request_context():
app = create_app()
return app.test_request_context()
Test code
def test_get_my_stuff(mocker, request_context):
def mock_Response_get(*args, **kwargs):
class mockResponse:
def __init__(self, status):
self.status_code = kwargs["status"]
def __call__(self, *args, **kwargs):
return self.status_code
resp = mockResponse(kwargs["status"])
return resp
with request_context:
mocker.patch("my_func.Response", side_effect=mock_Response_get)
response = my_modules.get_identity()
assert response.status_code == 500
Results
python -m pytest
pytestconfig = <_pytest.config.Config object at 0x7fa1025054c0>
def _mocker(pytestconfig: Any) -> Generator[MockerFixture, None, None]:
"""
Return an object that has the same interface to the `mock` module, but
takes care of automatically undoing all patches after each test method.
"""
result = MockerFixture(pytestconfig)
yield result
> result.stopall()
/usr/local/lib/python3.9/site-packages/pytest_mock/plugin.py:425:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/usr/local/lib/python3.9/site-packages/pytest_mock/plugin.py:99: in stopall
p.stop()
/usr/local/lib/python3.9/unittest/mock.py:1553: in stop
return self.__exit__(None, None, None)
/usr/local/lib/python3.9/unittest/mock.py:1522: in __exit__
delattr(self.target, self.attribute)
/usr/local/lib/python3.9/site-packages/werkzeug/local.py:316: in __get__
obj = instance._get_current_object() # type: ignore[misc]
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
def _get_current_object() -> T:
try:
obj = local.get() # type: ignore[union-attr]
except LookupError:
> raise RuntimeError(unbound_message) from None
E RuntimeError: Working outside of request context.
E
E This typically means that you attempted to use functionality that needed
E an active HTTP request. Consult the documentation on testing for
E information about how to avoid this problem.
/usr/local/lib/python3.9/site-packages/werkzeug/local.py:513: RuntimeError
To the best of my knowledge, running inside of the with request_context block should ensure against the "working outside of request context" error.
Am I doing something just stupid wrong here?
CodePudding user response:
With flask 2.2.2, pytest 7.2.0 and pytest-mock 3.7.0, I cannot reproduce your issue. That being said, result.stopall()
of the mocker fixture does get called after the test function ends, which also means after the test request context exits. You could try following...
- Have your fixture create the request context and
yield
nothing; theyield
means that your fixture would do a teardown as well, in which it would just exit the context:
@pytest.fixture()
def request_context():
with create_app().test_request_context():
yield
- Change the order in which fixtures are called (and teared down), putting your fixture before mocker. Also, don't create another test request context inside the test because that already exists:
def test_get_my_stuff(request_context, mocker):
def mock_Response_get(*args, **kwargs):
class mockResponse:
def __init__(self, status):
self.status_code = kwargs["status"]
def __call__(self, *args, **kwargs):
return self.status_code
resp = mockResponse(kwargs["status"])
return resp
mocker.patch("tests.app.Response", side_effect=mock_Response_get)
response = my_func()
assert response.status_code == 500