我正在尝试使用pytest为Flask应用编写单元测试。我有一个app工厂:
def create_app():
from flask import Flask
app = Flask(__name__)
app.config.from_object('config')
import os
app.secret_key = os.urandom(24)
from models import db
db.init_app(app)
return app
还有一个测试类:
class TestViews(object):
@classmethod
def setup_class(cls):
cls.app = create_app()
cls.app.testing = True
cls.client = cls.app.test_client()
@classmethod
def teardown_class(cls):
cls.app_context.pop()
def test_create_user(self):
"""
Tests the creation of a new user.
"""
view = TestViews.client.get(url_for('create_users')).status_code == 200
但是当我运行我的测试时,我收到以下错误:
RuntimeError: Attempted to generate a URL without the application context being pushed. This has to be executed when application context is available.
谷歌搜索告诉我(我认为)使用测试客户端应该创建一个自动应用程序上下文。我错过了什么?
答案 0 :(得分:1)
向测试客户端发出请求确实推送了应用程序上下文(间接)。但是,令人困惑的是,url_for
在视觉上位于测试请求调用中,并且认为它实际上是在内部调用的。首先评估url_for
调用,结果传递给client.get
。
url_for
通常用于在应用内生成网址,单元测试是外部。通常,您只需在请求中准确编写您要测试的URL,而不是生成它。
self.client.get('/users/create')
如果您真的想在此处使用url_for
,则必须在应用环境中执行此操作。请注意,当您处于应用程序上下文但不是请求上下文时,您必须设置SERVER_NAME
配置并传递_external=False
。但同样,你应该写出你想要测试的网址。
app.config['SERVER_NAME'] = 'localhost'
with self.app.app_context():
url = url_for(..., _external=False)
self.client.get(url, ...)
答案 1 :(得分:0)
您可以在使用 url_for()
方法创建的测试请求上下文中调用 app.test_request_context()
。有三种方法可以实现这一点。
既然你已经创建了setup和teardown方法,就像我通常用unittest做的那样,你可以在setup方法中推送一个测试请求上下文,然后在teardown方法中弹出它:
class TestViews(object):
@classmethod
def setup_class(cls):
cls.app = create_app()
cls.app.testing = True
cls.client = cls.app.test_client()
cls.context = cls.app.test_request_context() # create the context object
cls.context.push() # push the context
@classmethod
def teardown_class(cls):
cls.context.pop() # pop the context
def test_create_user(self):
"""
Tests the creation of a new user.
"""
view = TestViews.client.get(url_for('create_users')).status_code == 200
此外,您也可以只使用 pytest-flask。使用 pytest-flask,您可以在没有上下文管理器的情况下访问上下文绑定对象(url_for、request、session):
def test_app(client):
assert client.get(url_for('myview')).status_code == 200
如果不想安装插件,可以直接用下面的fixtures来做类似的事情(从pytest-flask的源码中偷来的):
@pytest.fixture
def app():
app = create_app('testing')
return app
@pytest.fixture(autouse=True)
def _push_request_context(request, app):
ctx = app.test_request_context() # create context
ctx.push() # push
def teardown():
ctx.pop() # pop
request.addfinalizer(teardown) # set teardown