使用 url_for 函数为 Flask 测试客户端生成 URL

rui*_*eco 7 python unit-testing pytest flask

我正在尝试使用 pytest 为 Flask 应用程序编写单元测试。我有一个应用工厂:

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
Run Code Online (Sandbox Code Playgroud)

和一个测试类:

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
Run Code Online (Sandbox Code Playgroud)

但是当我运行我的测试时,我收到以下错误:

RuntimeError: Attempted to generate a URL without the application context being pushed. This has to be executed when application context is available.
Run Code Online (Sandbox Code Playgroud)

谷歌搜索告诉我(我认为)使用测试客户端应该创建一个自动应用程序上下文。我错过了什么?

dav*_*ism 8

使用测试客户端发出请求确实会推送应用程序上下文(间接)。但是,您将url_for测试请求调用内部的可视化事实与实际上在内部调用的想法混淆了。该url_for呼叫首先进行评价的结果传递给client.get

url_for通常用于应用程序生成 URL ,单元测试是外部的。通常,您只需在请求中准确写入您要测试的 URL,而不是生成它。

self.client.get('/users/create')
Run Code Online (Sandbox Code Playgroud)

如果您真的想在url_for这里使用,则必须在应用程序上下文中进行。请注意,当您处于应用程序上下文而不是请求上下文中时,您必须设置SERVER_NAMEconfig 并通过_external=False. 但同样,您可能应该只写出您尝试测试的 URL。

app.config['SERVER_NAME'] = 'localhost'

with self.app.app_context():
    url = url_for(..., _external=False)

self.client.get(url, ...)
Run Code Online (Sandbox Code Playgroud)


Gre*_* Li 5

您可以url_for()在使用方法创建的测试请求上下文中调用app.test_request_context()。有三种方法可以实现这一目标。

通过设置和拆卸

由于您已经创建了设置和拆卸方法,就像我通常对单元测试所做的那样,您可以在设置方法中推送测试请求上下文,然后将其弹出到拆卸方法中:

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
Run Code Online (Sandbox Code Playgroud)

使用 pytest-flask

此外,您也可以只使用pytest-flask。使用 pytest-flask,您可以访问上下文绑定对象(url_for、request、session),而无需上下文管理器:

def test_app(client):
    assert client.get(url_for('myview')).status_code == 200
Run Code Online (Sandbox Code Playgroud)

带自用夹具

如果你不想安装该插件,你可以使用以下固定装置来做类似的事情(从 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
Run Code Online (Sandbox Code Playgroud)