pytest 每次迭代的新测试| for 循环 |参数化夹具

时间:2021-06-26 22:41:45

标签: python flask pytest

基本上,我试图对路由列表的每次迭代进行测试,以检查与特定函数关联的网页是否返回有效的状态代码。

我想要一些类似的东西:

import pytest
from flask import url_for
from myflaskapp import get_app

@pytest.yield_fixture
def app():
    # App context and specific overrides for test env
    yield get_app()

@pytest.yield_fixture
def client(app):
    yield app.test_client()

@pytest.yield_fixture
def routes(app):
    routes = [
    'foo',
    'bar',
    # There's quite a lot of function names here
    ]
    with app.app_context():
        for n, route in enumerate(routes):
            routes[n] = url_for(route)
            # yield url_for(route) #NOTE: This would be ideal, but not allowed.
            # convert the routes from func names to actual routes
    yield routes

@pytest.mark.parametrize('route', routes)
def test_page_load(client, route):
    assert client.get(route.endpoint).status_code == 200

我了解到,由于解释/加载/执行顺序方面的原因,您不能将参数化与夹具混合作为参数,但是,如何根据“最佳实践”解决这个问题?

我看到了一个解决方案,您可以直接从函数生成测试,这看起来非常灵活,可能符合我想要的Passing pytest fixture in parametrize(虽然我不能直接使用调用夹具装饰的函数,所以可能不会)

虽然,我是 pytest 的新手,我很想看到更多关于如何在几乎没有限制的情况下在迭代中生成测试或执行多个测试的示例,同时坚持正确的 pytest 样式和 DRY 原则。 (我知道 conftest.py)

如果重要的话,我会优先考虑多功能性/实用性而不是适当的样式。 (在合理范围内,可维护性也是一个高优先级)

我希望能够参考此问题的解决方案,以帮助指导我将来如何处理构建测试的问题,但我似乎不断遇到障碍/限制,或者被 pytest 告知我无法执行 X 解决方案我也希望/想要的方式。

相关帖子:

  1. 干燥:pytest: parameterize fixtures in a DRY way
  2. 从函数生成测试:Passing pytest fixture in parametrize
  3. 非常简单的解决方案(不适用于这种情况):Parametrize pytest fixture
  4. PyTest 中的 Flask 应用上下文:Testing code that requires a Flask app or request context
  5. 避免具有多个列表装置的边缘情况:Why does Pytest perform a nested loop over fixture parameters

2 个答案:

答案 0 :(得分:1)

我目前摸索的解决方案是这样的:

import pytest
from flask import url_for
from myflaskapp import get_app

@pytest.fixture
def app():
    app = get_app()
    # app context stuff trimmed out here
    return app

@pytest.fixture
def client(app):
    client = app.test_client()
    return client

def routes(app):
    '''GET method urls that we want to perform mass testing on'''
    routes = ['foo', 'bar']
    with app.app_context():
        for n, route in enumerate(routes):
            routes[n] = url_for(route)
    return routes

@pytest.mark.parametrize('route', routes(get_app()))
#NOTE: It'd be really nice if I could use routes as a 
# fixture and pytest would handle this for me. I feel like I'm
# breaking the rules here doing it this way. (But I don't think I actually am)
def test_page_load(client, route):
    assert client.get(route.endpoint).status_code == 200

我对这个解决方案的最大问题是,我不能直接将夹具作为函数调用,而这个解决方案要么需要那样做,要么在夹具之外完成我的夹具所做的所有工作,这并不理想。我希望能够参考此解决方案来解决我将来如何构建测试。

对于任何想要专门复制我的 FLASK 解决方案的人:

我目前的解决方案对某些人来说可能比对我更糟糕,我对 get_app() 使用单例结构,因此在我的情况下多次调用 get_app() 应该没问题,因为如果全局变量尚未定义,它将调用 create_app() 并将应用程序本身存储为全局变量,基本上模拟只调用一次 create_app() 的行为。

答案 1 :(得分:1)

Pytest 固定装置本身可以参数化,但不能使用 pytest.mark.parametrize。 (看起来这类问题也有人回答了 here。)所以:

import pytest
from flask import url_for
from myflaskapp import get_app

@pytest.fixture
def app():
    app = get_app()
    # app context stuff trimmed out here
    return app

@pytest.fixture
def client(app):
    client = app.test_client()
    return client

@pytest.fixture(params=[
    'foo', 
    'bar'
])
def route(request, app):
    '''GET method urls that we want to perform mass testing on'''
    with app.app_context():
        return url_for(request.param)


def test_page_load(client, route):
    assert client.get(route.endpoint).status_code == 200

The documentation 这样解释:

<块引用>

Fixture 函数可以被参数化,在这种情况下,它们将被多次调用,每次执行一组相关测试,即。 e.依赖于这个夹具的测试。测试函数通常不需要知道它们的重新运行。夹具参数化有助于为组件编写详尽的功能测试,这些组件本身可以通过多种方式进行配置。

扩展前面的例子,我们可以标记夹具以创建两个 smtp_connection 夹具实例,这将导致使用夹具的所有测试运行两次。 Fixture 函数通过特殊的请求对象访问每个参数:

# content of conftest.py
import pytest import smtplib


@pytest.fixture(scope="module", params=["smtp.gmail.com", "mail.python.org"])
def smtp_connection(request):
    smtp_connection = smtplib.SMTP(request.param, 587, timeout=5)
    yield smtp_connection
    print("finalizing {}".format(smtp_connection))
    smtp_connection.close()

主要的变化是使用@pytest.fixture 声明params,这是一个值列表,fixture 函数将执行每个值,并且可以通过request.param 访问一个值。无需更改测试功能代码。

相关问题