10

不是在谈论Parameterizing a fixture功能,该功能允许针对硬编码的一组参数运行多次fixture。

我有很多遵循以下模式的测试:

httpcode = 401  # this is different per call
message = 'some message'  # this is different per call
url = 'some url'  # this is different per call


mock_req = mock.MagicMock(spec_set=urllib2.Request)
with mock.patch('package.module.urllib2.urlopen', autospec=True) as mock_urlopen, \
     mock.patch('package.module.urllib2.Request', autospec=True) as mock_request:
    mock_request.return_value = mock_req
    mock_urlopen.side_effect = urllib2.HTTPError(url, httpcode, message, {}, None)
    connection = MyClass()
    with pytest.raises(MyException):
        connection.some_function()  # this changes

本质上,我有一个 API 客户端类,它包含自定义的、有意义的异常,这些异常将 urllib2 错误包装在特定于 API 的东西中。所以,我有这个模式——修补一些方法,并在其中一个上设置副作用。我可能在十几个不同的测试中使用它,唯一的区别是在 side_effect 中使用的三个变量,以及我调用的 MyClass() 方法。

有没有办法让它成为一个 pytest 夹具并传入这些变量?

4

4 回答 4

18

您可以使用间接夹具参数化 http://pytest.org/latest/example/parametrize.html#deferring-the-setup-of-parametrized-resources

@pytest.fixture()
def your_fixture(request):
    httpcode, message, url = request.param
    mock_req = mock.MagicMock(spec_set=urllib2.Request)
    with mock.patch('package.module.urllib2.urlopen', autospec=True) as mock_urlopen, \
         mock.patch('package.module.urllib2.Request', autospec=True) as mock_request:
        mock_request.return_value = mock_req
        mock_urlopen.side_effect = urllib2.HTTPError(url, httpcode, message, {}, None)
        connection = MyClass()
        with pytest.raises(MyException):
            connection.some_function()  # this changes


@pytest.mark.parametrize('your_fixture', [
    (403, 'some message', 'some url')
], indirect=True)
def test(your_fixture):
   ...

并且 your_fixture 将在使用所需参数进行测试之前运行

于 2015-01-20T18:37:26.570 回答
6

自从发布我的问题以来,我已经对此进行了更多研究,我能想到的最好的方法是:

固定装置不能以这种方式工作。只需使用常规功能,即:

def my_fixture(httpcode, message, url):
    mock_req = mock.MagicMock(spec_set=urllib2.Request)
    with mock.patch('package.module.urllib2.urlopen', autospec=True) as mock_urlopen, \
         mock.patch('package.module.urllib2.Request', autospec=True) as mock_request:
        mock_request.return_value = mock_req
        mock_urlopen.side_effect = urllib2.HTTPError(url, httpcode, message, {}, None)
        connection = MyClass()
        return (connection, mock_request, mock_urlopen)

def test_something():
    connection, mock_req, mock_urlopen = my_fixture(401, 'some message', 'some url')
    with pytest.raises(MyException):
        connection.some_function()  # this changes
于 2015-01-30T23:11:49.657 回答
1

如何将参数传递给夹具?

暂时解开这个想法:你要求一个夹具,它是一个函数,它对参数做出反应。因此,返回一个对参数做出反应的函数:

@pytest.fixture
def get_named_service():
    def _get_named_service(name):
        result = do_something_with_name(name)
        return result
    return _get_named_service

因此,在测试中,您可以为函数提供参数:

def test_stuff(get_named_service):
    awesome_service = get_named_service('awesome')
    terrible_service = get_named_service('terrible')
    # Now you can do stuff with both services.

这被记录为工厂模式:
https ://docs.pytest.org/en/latest/how-to/fixtures.html#factories-as-fixtures

正如 OP 所发现的,这只是一个函数,但其​​优点是位于conftest所有其他常见实用程序和设置/拆卸代码所在的位置;加上自我记录测试的依赖关系。

于 2020-04-29T01:42:29.457 回答
0

我知道这已经过时了,但也许它可以帮助那些再次遇到这个问题的人

@pytest.fixture
def data_patcher(request):

    def get_output_test_data(filename, as_of_date=None):
         # a bunch of stuff to configure output
        return output

    def teardown():
        pass

    request.addfinalizer(teardown)

    return get_output_test_data

然后,在函数内部:

with patch('function to patch', new=data_patcher):
于 2018-01-17T17:53:33.290 回答