我正在尝试迁移到py.test以便于使用和自动发现测试.当我使用unittest运行测试时,测试工作正常.当我在py.test下运行测试时,我得到了RuntimeError: working outside of application context
.
这是测试代码(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()
这是我正在测试的精简文件(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, '/')
正如您所看到的,这与烧瓶站点上的文档非常相似:测试骨架,实际上,当我使用unittest运行它时,它成功:
$ python tmp1/test_app.py . ---------------------------------------------------------------------- Ran 1 test in 0.115s OK $
但是,当我用py.test测试时,它失败了:
$ ./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 =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 ======================
现在,事实证明,我可以通过这样做来使这个测试通过:
$ rm tmp1/__init__.py
并通过这样做使它再次失败:
$ touch tmp1/__init__.py
那么,unittest和py.test处理模块中文件的方式有什么区别吗?看起来非常奇怪,它足以让Flask抱怨,因为我显然是在调用app.test_client().get()的应用程序上下文中.这是预期的行为,还是应该针对py.test提出错误?
如果它是相关的,我从父目录执行测试的原因是因为我没有能力将模块添加到site-packages,所以我从父目录启动我的所有代码,我在那里我安装了Flask,py.test等.
编辑:解决了.这是一个安装问题.添加pythonpath标记,因为那是解决方案.
您可以手动设置应用上下文:
app = Flask(__name__) ctx = app.app_context() ctx.push() with ctx: pass
不直接回答TS问题,但主要是针对"应用程序上下文"错误.
在setUp和tearDown函数中添加推送和弹出上下文应该有助于解决此错误:
def setUp(self): self.app_context = app.app_context() self.app_context.push() def tearDown(self): self.app_context.pop()
您可以在那里找到有关烧瓶背景的更多信息:
http://flask.pocoo.org/docs/appcontext/
http://flask.pocoo.org/docs/reqcontext/
丹尼尔克罗诺维特在这篇很棒的文章中也写道:
http://kronosapiens.github.io/blog/2014/08/14/understanding-contexts-in-flask.html
PS如果您计划在测试中使用url_for,则需要进行其他配置:
@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)