"RuntimeError: working outside of application context" when unit testing with py.test
Solution 1
In case it's relevant, the reason I'm executing the tests from the parent directory is because I don't have the ability to add modules to site-packages, so I'm initiating all my code from the parent directory, where I've installed Flask, py.test, etc.
It turns out that this was very relevant. Because my code would eventually be run by a daemon, I wasn't sure I could depend on PYTHONPATH, and I didn't want to change sys.path at the top of each file. So, I was installing the package tarballs, and adding symlinks where necessary to make the packages importable.
It turns out the structure required to make the unittest code find flask was:
./Flask-0.10.1/
./flask -> Flask-0.10.1/flask/
./tmp1/flask -> ../Flask-0.10.1/flask/
The last line was required by unittest, and it was that symlink that broke py.test.
When I just put the packages in their own directory, and set the PYTHONPATH, everything works. If I can't control the environment to use PYTHONPATH for the daemon, I'll bite the bullet, and add the sys.path modication everywhere.
So, bottom line is, it was an installation problem.
Solution 2
Not directly answer to TS question, but mostly for 'application context' error.
Adding pushing and poping context in setUp and tearDown functions should help with this error:
def setUp(self):
self.app_context = app.app_context()
self.app_context.push()
def tearDown(self):
self.app_context.pop()
You can find more info on flask context there:
also in this awesome article by Daniel Kronovet:
PS If you planning to use url_for in tests, additional configuration required:
@classmethod
def setUpClass(cls)
app.config['SERVER_NAME'] = 'localhost:5000'
Example
class ViewsTestCase(unittest.TestCase):
@classmethod
def setUpClass(cls):
app.config['SERVER_NAME'] = 'localhost:5000'
cls.client = app.test_client()
def setUp(self):
self.app_context = app.app_context()
self.app_context.push()
def tearDown(self):
self.app_context.pop()
def test_view_should_respond(self):
r = self.client.get(url_for("index"))
self.assertEqual(r.status_code, 200)
Solution 3
You can set app context manually:
app = Flask(__name__)
ctx = app.app_context()
ctx.push()
with ctx:
pass
John Hazen
Coding let's me be creative. I also enjoy creativity in music, woodworking, and growing things.
Updated on July 12, 2022Comments
-
John Hazen almost 2 years
I'm trying to migrate to py.test for the ease of use and auto-discovery of tests. When I run my tests with unittest, the test works fine. When I run the test under py.test, I get
RuntimeError: working outside of application context
.Here's the test code (test_app.py):
import unittest from app import app class TestAPILocally(unittest.TestCase): def setUp(self): self.client = app.test_client() def testRoot(self): retval = self.client.get('/').data self.assertTrue('v1' in retval) if __name__ == '__main__': unittest.main()
And here's the stripped down file I'm testing (app.py):
from flask import Flask from flask.ext.restful import Api, Resource class APIListAPI(Resource): def get(self): return ['v1'] app = Flask(__name__) api = Api(app) api.add_resource(APIListAPI, '/')
As you can see, this is very similar to the docs on the flask site: the testing skeleton, and indeed, when I run it with unittest, it succeeds:
$ python tmp1/test_app.py . ---------------------------------------------------------------------- Ran 1 test in 0.115s OK $
But, when I test with py.test, it fails:
$ ./py.test tmp1/test_app.py =================== test session starts ========================= platform sunos5 -- Python 2.7.5 -- py-1.4.22 -- pytest-2.6.0 collected 1 items tmp1/test_app.py F ========================= FAILURES ============================== _________________ TestAPILocally.testRoot _______________________ self = <tmp1.test_app.TestAPILocally testMethod=testRoot> def testRoot(self): > retval = self.client.get('/').data tmp1/test_app.py:10: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ werkzeug/test.py:762: in get return self.open(*args, **kw) flask/testing.py:108: in open follow_redirects=follow_redirects) werkzeug/test.py:736: in open response = self.run_wsgi_app(environ, buffered=buffered) werkzeug/test.py:659: in run_wsgi_app rv = run_wsgi_app(self.application, environ, buffered=buffered) werkzeug/test.py:855: in run_wsgi_app app_iter = app(environ, start_response) tmp1/flask/app.py:1836: in __call__ return self.wsgi_app(environ, start_response) tmp1/flask/app.py:1820: in wsgi_app response = self.make_response(self.handle_exception(e)) flask_restful/__init__.py:256: in error_router if self._has_fr_route(): flask_restful/__init__.py:237: in _has_fr_route if self._should_use_fr_error_handler(): flask_restful/__init__.py:218: in _should_use_fr_error_handler adapter = current_app.create_url_adapter(request) werkzeug/local.py:338: in __getattr__ return getattr(self._get_current_object(), name) werkzeug/local.py:297: in _get_current_object return self.__local() _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ def _find_app(): top = _app_ctx_stack.top if top is None: > raise RuntimeError('working outside of application context') E RuntimeError: working outside of application context flask/globals.py:34: RuntimeError ================ 1 failed in 1.02 seconds ======================
Now, it turns out, I can make this test pass just by doing this:
$ rm tmp1/__init__.py
And make it fail again by doing this:
$ touch tmp1/__init__.py
So, is there some difference between the way that unittest and py.test handles files in modules? It seems very strange that it breaks enough to make Flask complain, as I clearly am in an app context calling app.test_client().get(). Is this expected behavior, or should I file a bug against py.test?
In case it's relevant, the reason I'm executing the tests from the parent directory is because I don't have the ability to add modules to site-packages, so I'm initiating all my code from the parent directory, where I've installed Flask, py.test, etc.
Edit: Solved. It was an installation problem. Adding pythonpath tag, since that was the solution.
-
matthewlent about 9 yearsI needed to remove the .test_client() from my app to get this to work.
-
varela over 8 yearsYou can use virtualenv with virtualenvwrapper to add folders to sys.path globally for the whole application and avoid doing this in code