为Flask测试客户端生成URL

时间:2017-10-09 12:34:50

标签: python unit-testing flask

我正在尝试使用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.

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

2 个答案:

答案 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。使用 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