首页 > 解决方案 > Pytest - 是否可以创建一个过滤另一个夹具的夹具?

问题描述

我有这个代码(谢谢,MT):

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

这工作正常,但我得到了所有那些“成功”的测试False案例if

有没有办法过滤掉它们?

我尝试创建一个过滤掉成功的夹具:

@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 等也是如此)

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

ValueError:要解包的值太多(预期为 2)

所以我尝试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

TypeError:“函数”对象不可迭代

我还在学习如何使用固定装置...

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

标签: pytestfixtures

解决方案


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

  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")


推荐阅读