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.
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'
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)