Pytest - 是否可以创建一个过滤另一个夹具的夹具?

Pytest - is it possible to create a fixture that filters another fixture?

我有这个代码(谢谢,M.T):

url_list = [
    ['example1.com/x', 'example2.com/x'],
    ['example1.com/y', 'example2.com/y'],
    ['example1.com/z', 'example2.com/z'],
    ['example1.com/v', 'example2.com/v'],
    ['example1.com/w', 'example2.com/w'],
]

@pytest.fixture(scope="session", params=url_pairs)
def response_pairs(request):
    url_a, url_b = request.param
    yield requests.get(url_a), requests.get(url_b)

def test_1(response_pairs):
    response_a, response_b = response_pairs
    assert response_a.status_code == response_b.status_code
    except Exception as e:
        yield e, None

@pytest.mark.parametrize("field", ["one", "two", "three", "four", "five"])
def test_2(field, response_pairs):
    body_a = response_pairs[0].json()
    body_b = response_pairs[1].json()
    assert body_a[field] == body_b[field]

目前为止一切正常。

现在,我希望 test_2 根据 status_code 做不同的事情。

所以我创建了 test_2 的多个副本,并为每个副本添加了 if 语句:

@pytest.mark.parametrize("field", ["one", "two", "three", "four", "five"])
def test_200(field, response_pairs):
    response_a, response_b = response_pairs
    if response_a.status_code == response_b.status_code == 200:
        body_a = response_a.json()
        body_b = response_b.json()
        assert body_a[field] == body_b[field]

@pytest.mark.parametrize("field", ["reason", "text"])
def test_404(field, response_pairs):
    response_a, response_b = response_pairs
    if response_a.status_code == response_b.status_code == 404:
        assert getattr(response_a, field, None).lower() == getattr(response_b, field, None).lower()

@pytest.mark.parametrize("field", ["reason", "text"])
def test_error(field, response_pairs):
    response_a, response_b = response_pairs
    if response_a.status_code == response_b.status_code and response_a.status_code not in [200, 404]:
        assert getattr(response_a, field, None).lower() == getattr(response_b, field, None).lower()

这工作正常,但我得到了所有那些 ifs

False 案例的“成功”测试

有什么方法可以过滤掉它们吗?

我尝试创建一个过滤掉成功的装置:

@pytest.fixture
def responses_200(response_pairs):
    for response_a, response_b in response_pairs:
        if response_a.status_code == response_b.status_code == 200:
            yield response_a, response_b

(404s 等也一样)

但这不起作用。它并没有摆脱无意义的测试,而是一切都失败了:

所以我尝试从参数列表中删除 response_pairs

@pytest.fixture
def responses_200(request):
    for response_a, response_b in response_pairs:
        if response_a.status_code == response_b.status_code == 200:
            yield response_a, response_b

现在安装失败 TypeError: 'function' object is not iterable:

我还在学习如何使用 fixtures...

是否可以创建一个过滤另一个夹具的夹具?

在没有重现您的错误的情况下,关于观察到的错误的两点评论:

  1. 您需要将要使用的灯具名称添加到参数列表中(在本例中为 response_pairs)。如果删除它,则在函数中引用其名称返回的值不是您期望的夹具值,而是函数本身(因此出现您试图遍历函数的错误消息)
  2. 在任何给定的测试中,我希望 response_pairs 只包含一对。因此,我会尝试删除 for 循环,然后像这样解压那一对:
@pytest.fixture
def responses_200(response_pairs):
    response_a, response_b = response_pairs
    if response_a.status_code == response_b.status_code == 200:
        yield response_a, response_b

我希望这样可以避免观察到的错误,但我不确定它是否会产生仅针对状态 200 响应执行测试的预期结果。

一种至少可以避免执行测试的有点老套的方法是从夹具内部跳过它们:

简化示例:

import pytest

rl_list = [
    ['example1.com/x', 200],
    ['example1.com/y', 404],
    ['example1.com/z', 200],
]

@pytest.fixture(scope="session", params=rl_list)
def url_response_pair(request):
    url, response = request.param
    yield url, response

@pytest.fixture(scope="session")
def response_200(url_response_pair):
    url, response = url_response_pair
    if response != 200:
        pytest.skip('Skipping 200 test for response code {response} from url {url}')

def test_something_for_response_200(response_200):
    print("testing something")