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

2024-04-26 22:53:22 发布

您现在位置:Python中文网/ 问答频道 /正文

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

我想要的东西大致如下:

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(虽然我不能直接使用调用fixture修饰函数,所以可能不会)

尽管如此,我还是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

Tags: 函数infromimportclientappurlfor
2条回答

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

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

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

对于任何希望复制我的烧瓶溶液的人:

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

Pytest装置本身可以参数化,但不能使用pytest.mark.parametrize。(这类问题似乎也得到了回答。)因此:

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 functions can be parametrized in which case they will be called multiple times, each time executing the set of dependent tests, i. e. the tests that depend on this fixture. Test functions usually do not need to be aware of their re-running. Fixture parametrization helps to write exhaustive functional tests for components which themselves can be configured in multiple ways.

Extending the previous example, we can flag the fixture to create two smtp_connection fixture instances which will cause all tests using the fixture to run twice. The fixture function gets access to each parameter through the special request object:

# 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()

The main change is the declaration of params with @pytest.fixture, a list of values for each of which the fixture function will execute and can access a value via request.param. No test function code needs to change.

相关问题 更多 >