Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"RuntimeError: working outside of application context" when unit testing with py.test

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.

like image 793
John Hazen Avatar asked Jul 22 '14 00:07

John Hazen


People also ask

How do I fix RuntimeError working outside of request context?

RuntimeError: Working outside of application context. This typically means that you attempted to use functionality that needed to interface with the current application object in some way. To solve this, set up an application context with app. app_context().

How do I push an application into a context Flask?

Flask automatically pushes an application context when handling a request. View functions, error handlers, and other functions that run during a request will have access to current_app . Flask will also automatically push an app context when running CLI commands registered with Flask. cli using @app.

What is request context in Flask?

The request context keeps track of the request-level data during a request. Rather than passing the request object to each function that runs during a request, the request and session proxies are accessed instead.

What is Flask current_app?

current_app is function in Flask's flask. globals module and is an instance of LocalProxy from the Werkzeug framework. current_app can be used to access data about the running application, including the configuration. This is useful for both developers using the framework and ones building extensions for Flask.


1 Answers

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:

  • http://flask.pocoo.org/docs/appcontext/
  • http://flask.pocoo.org/docs/reqcontext/

also in this awesome article by Daniel Kronovet:

  • http://kronosapiens.github.io/blog/2014/08/14/understanding-contexts-in-flask.html

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)
like image 95
MadisonTrash Avatar answered Oct 19 '22 05:10

MadisonTrash