使用 g 的 Flask pytest 测试函数

che*_*dog 6 pytest flask python-3.x

我正在使用Flask Cookiecutter构建测试,并且所有其他测试都工作正常。仅当我测试使用元素的函数时才会出现问题g,例如g.user在 view.py 文件中。

我的conftest.py与 Flask-cookiecutter 的完全相同,加上来自Flask-Testing 的伪造资源和上下文技巧的添加。

### conftest.py ###
...
from contextlib import contextmanager
from flask import appcontext_pushed, g

@contextmanager
def user_set(app, user):
    def handler(sender, **kwargs):
        g.user = user

    with appcontext_pushed.connected_to(handler, app):
        yield
...
Run Code Online (Sandbox Code Playgroud)

这是我的测试文件:

### test_file.py ###
from .conftest import user_set
import pytest
from my_app.utils import presave_posted_settings # <-- fn I want to test

@pytest.fixture()
def form_data():
    return {...bunch of data..}

@pytest.mark.usefixtures("form_data")
class TestPresaveSettings:
    """Test cases for checking attributes before saving"""
    def test_presave_posted_settings(self, form_data, user, testapp):
        """User meals are selected in form"""
        with user_set(testapp, user):  #<-- testapp and user available from flask-cookiecutter conftest.py
            assert presave_posted_settings(form_data)
Run Code Online (Sandbox Code Playgroud)

当我在 上运行测试时test_file.py,我看到:

user = <User 'user0'>, testapp = <webtest.app.TestApp object at 0x1101ee160>

def test_presave_posted_settings(self, form_data, user, testapp):
    """User meals are selected in form"""
    with user_set(testapp, user):
>           assert presave_posted_settings(form_data)

test_utils.py:20: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _  
../my_app/utils.py:26: in presave_posted_settings
g.user.breakfast = g.user.lunch = g.user.dinner = g.user.snack = g.user.dessert = False
 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _  
 self = <flask.g of 'my_app'>, name = 'user'

 def __getattr__(self, name):
    if name == '__members__':
        return dir(self._get_current_object())
>       return getattr(self._get_current_object(), name)
E       AttributeError: '_AppCtxGlobals' object has no attribute 'user'
Run Code Online (Sandbox Code Playgroud)

我用谷歌搜索过,大多数示例都使用 Unittest 或将所有 pytest 测试放在一个文件中。我想坚持拥有多个测试文件的模式,每个测试文件都利用conftest.py,但我一生都无法弄清楚如何设置它,以便我可以测试使用该g元素的函数。谢谢!

小智 2

这个问题很老了,但与我遇到的问题最相关,我有一个简单的解决方案。

给定测试客户端的固定装置,如下所示(请原谅温和的伪代码):

@pytest.fixture()
def test_client():
    app = create_app()
    with app.test_client() as test_client:
        with app.app_context():
            yield test_client
Run Code Online (Sandbox Code Playgroud)

我尝试创建一个修改 g 的固定装置,def modify_g(test_client)认为依赖test_client于此提供的上下文将使其可用。由于我可能不完全理解 pytest 如何评估装置,情况并非如此。你会得到同样的'_AppCtxGlobals' object has no attribute错误。但是,如果您创建一个中间函数,它会正确评估,并且修改后的g对象将出现在测试上下文中。

@pytest.fixture()
def pre_test_client():
    app = create_app()
    with app.test_client() as test_client:
        with app.app_context():
            yield test_client

@pytest.fixture()
def test_client(pre_test_client):
    yield pre_test_client

@pytest.fixture()
def modify_g(pre_test_client):
    g.user = 'test'
Run Code Online (Sandbox Code Playgroud)

如果定义了,那么使用 g 的测试将起作用:

def test_g_data(test_client, modify_g):
Run Code Online (Sandbox Code Playgroud)

这意味着模拟 g 数据中的许多不同状态并将它们作为固定装置放入测试函数中非常容易。

诚然,我不明白为什么这是必要的,所以我希望能对 pytest 如何评估装置进行说明。

请注意,这会忽略appcontext_pushedFlask 提供的解决方案,但这是一种非常快速且简单的使其工作的方法,尽管可能不是最佳解决方案。