繁体   English   中英

Pytest:仅测试参数化夹具的一个实例

[英]Pytest: test only one instance of parametrized fixture

环境

假设我有conftest.py ,我在其中定义了测试资源

class ClassifyRequest:
    pass

class OCRRequest:
    pass

@pytest.fixture(params=[ClassifyRequest, OCRRequest])
def api_request(request):
    return request.param()  # return one of the request instances

然后,在test_api.py中,我使用参数化夹具来测试服务:

def test_service(api_request):
    response = send_request(api_request)
    assert response.ok()

一切都很好,但我想测试专门的夹具api_request[ClassifyRequest]

@pytest.mark.usefixture("api_request[ClassifyRequest]")
def test_classification():
    # do something with the api_request fixture

为测试功能专门化参数化夹具的方法是什么? 我有两个想法:

  1. 只需使用非参数化夹具。 这不可避免地会导致样板代码。
  2. 从夹具装饰器中删除显式参数化并使用间接参数化,如
    @pytest.mark.parametrize("response_class", ["classify", "ocr"]) def test_api(api_request): # do smth
    用字符串替换类参数等于创建两个配置源。 如果我想添加另一个参数怎么办? 我是否必须为它设计一个新的字符串,以及在api_request fixture 中间接实例化一个对象?
  3. 相同,除了保留类参数化并将类conftest.py之外,因为 pytest 无法使用 import 语句从该模块导入名称。

附加信息

pytest==6.0.1

如果您的所有夹具都是实例化,我会选择参数化测试,而不是夹具。 但是,好吧,如果您必须手动对其进行专门化,您可以简单地添加一个包装器以使夹具可调用:

import pytest
import types

class ClassifyRequest:
    def __init__(self):
        print("ClassifyRequest ctor")

class OCRRequest:
    def __init__(self):
        print("OCRRequest ctor")

def api_request_wrapper(request):
    return request.param()  # return one of the request instances

@pytest.fixture(params=[ClassifyRequest, OCRRequest])
def api_request(request):
    return api_request_wrapper(request)

def test_classification():
    request = types.SimpleNamespace()
    request.param = ClassifyRequest
    instance = api_request_wrapper(request)

不过,似乎有点hacky。

您要实现的是创建测试用例过滤规则。 最好的方法是通过 Pytest 中的“pytest_collection_modifyitems”挂钩。

一旦从您作为输入提供给 Pytest 的路径收集了所有测试,就会调用此钩子。

你可以把你的逻辑放在这个钩子里。

将 ID 与您的参数相关联始终是最佳实践,如下所示:

@pytest.fixture(params=[ClassifyRequest, OCRRequest], 
                ids=['ClassifyRequest', 'OCRRequest'])
def api_request(request):
    return request.param()  # return one of the request instances

一旦你有了这个,你的测试用例将生成为 test_classification[ClassifyRequest] 和 test_classification['OCRRequest']..

如果您正在运行特定的测试集并希望过滤应用于所有测试,那么 -k 选项本身就足够了。

但是因为这里它只在你想要应用规则的测试子集上,所以钩子定义可以在测试所在的模块中。

注意:这是一个初步的结论,我在 pytest Github 上创建了一个issue来看看社区对这个话题的看法。


我认为实现所需专业化的最简洁方法是将请求类移动到单独的模块并使用conftest.py仅声明夹具功能 根据 pytest 文档

如果在实施测试期间您意识到要使用多个测试文件中的夹具 function,您可以将其移动到conftest.py文件。

如果您想让文件中的测试数据可用于您的测试,一个好方法是将这些数据加载到夹具中以供您的测试使用。

这样我可以在测试模块中导入类并通过测试函数间接参数化夹具。 在我的情况下,请求类很容易实例化,因此不需要为每个 class 单独的夹具,只需要聚合。 但一般来说,为每个资源定义一个夹具和一个聚合参数化夹具是可以的。

您还可以为ClassifyRequest分离固定装置,并将其用于分类测试api_request固定装置。

假设请求的实例化是廉价的。 如果它们很贵,您可以考虑更改固定装置的范围。

@pytest.fixture
def classify_request():
    return ClassifyRequest()  # return ClassifyRequest instance

@pytest.fixture
def ocr_request():
    return OCRRequest()  # return OCRRequest instance

@pytest.fixture(params=["classify", "ocr"])
def api_request(request, classify_request, ocr_request):
    # return one of the request instances
    if request.param == "classify":
         return classify_request
    elif request.param == "ocr":
         return ocr_request
    assert False

否则(这是对您的想法的轻微修改),将您想要的类列入白名单test_classification

@pytest.fixture(params=[ClassifyRequest, OCRRequest])
def clazz(request):
    return request.param

@pytest.fixture
def api_request(clazz):
    return clazz()

@pytest.mark.parametrize("clazz", [ClassifyRequest], indirect=True)
def test_classification(api_request):
    # do smth

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM