在pytest中参数化测试和夹具时,pytest似乎热切地评估所有参数,并在开始执行测试之前构造一些测试列表数据结构。
在以下两种情况下,这是一个问题:
因此,我的问题是:是否可以告诉pytest在飞行中评估参数(即懒惰)?
发布于 2019-07-21 23:22:11
至于你在评论链接中提出的两个问题,手册似乎就是一个人应该做的事情。它允许“只在实际测试运行时设置昂贵的资源,如DB连接或子进程”。
但对于一个问题,似乎没有实现这样的功能。您可以将生成器直接传递给parametrize,如下所示:
@pytest.mark.parametrize('data', data_gen)
def test_gen(data):
...但是pytest将list()的生成器->内存问题也在这里继续存在。
我还发现了一些github 问题,而不是更多地了解为什么 pytest,而不是懒洋洋地处理生成器。这似乎是设计上的问题。因此,“不可能正确地管理参数化,将生成器作为值”,因为
"pytest必须收集所有元数据的所有测试.收集总是在测试运行之前发生“。
在这种情况下,也有一些是指hypothesis或nose's yield-base tests。但是,如果您仍然想坚持使用pytest,那么有一些解决办法:
import pytest
def get_data(N):
for i in range(N):
yield list(range(N))
N = 3000
data_gen = get_data(N)@pytest.mark.parametrize('ind', range(N))
def test_yield(ind):
data = next(data_gen)
assert data在这里,您将参数化到index (这并不是很有用的--仅仅是指示它必须执行的数量最多),并在下一次运行中生成数据。您也可以将其包装到memory_profiler中。
Results (46.53s):
3000 passed
Filename: run_test.py
Line # Mem usage Increment Line Contents
================================================
5 40.6 MiB 40.6 MiB @profile
6 def to_profile():
7 76.6 MiB 36.1 MiB pytest.main(['test.py'])和直截了当的比较:
@pytest.mark.parametrize('data', data_gen)
def test_yield(data):
assert data它“吃掉”更多的记忆:
Results (48.11s):
3000 passed
Filename: run_test.py
Line # Mem usage Increment Line Contents
================================================
5 40.7 MiB 40.7 MiB @profile
6 def to_profile():
7 409.3 MiB 368.6 MiB pytest.main(['test.py'])data_gen = get_data(N)
@pytest.fixture(scope='module', params=len_of_gen_if_known)
def fix():
huge_data_chunk = next(data_gen)
return huge_data_chunk
@pytest.mark.parametrize('other_param', ['aaa', 'bbb'])
def test_one(fix, other_param):
data = fix
...因此,我们在这里使用module作用域级别的夹具,以便为参数化测试“预先设置”我们的数据。请注意,在这里您可以添加另一个测试,它也将接收生成的数据。只需在test_two之后添加它:
@pytest.mark.parametrize('param2', [15, 'asdb', 1j])
def test_two(fix, param2):
data = fix
...注意:如果您不知道生成的数据的数量,可以使用这个技巧:设置一些近似值(如果它比生成的测试计数高一点的话)和“标记”测试,如果它在StopIteration中停止,那么它将在所有生成的数据都生成时发生。
try产量在您的测试,直到它还没有结束。但是这里还有另一个缺点-- pytest将它作为单个测试(可能包含大量的内部检查)来处理,如果生成的数据中有一个失败,它就会失败。换句话说,如果与参数化方法相比,并不是所有的pytest统计/特性都可以访问。pytest.main(),如下所示:# data_generate
# set_up test
pytest.main(['test'])@pytest.mark.parametrize("one", list_1)
@pytest.mark.parametrize("two", list_2)
def test_maybe_convert_objects(self, one, two):
...改为:
@pytest.mark.parametrize("one", list_1)
def test_maybe_convert_objects(self, one):
for two in list_2:
...它类似于工厂,但更容易实现。此外,它不仅减少了RAM的多次,而且收集的时间,以及。这里的缺点-对于pytest,它将是对所有two值的一个测试。它与“简单”测试一起顺利工作--如果一个人的内部有一些特殊的xmark或者其他什么东西,那么可能会出现问题。
我还打开了相应的问题,可能会出现一些关于这个问题的额外信息/调整。
发布于 2020-06-12 16:05:02
编辑:我的第一个反应是“这正是参数化的夹具”:函数作用域的是--在执行测试节点之前调用懒惰值,并且通过参数化夹具,您可以根据需要预定义任意数量的变量(例如,从数据库键列表中)。
from pytest_cases import fixture_plus
@fixture_plus
def db():
return <todo>
@fixture_plus
@pytest.mark.parametrize("key", [<list_of keys>])
def sample(db, key):
return db.get(key)
def test_foo(sample):
return sample尽管如此,在某些(罕见的)情况下,您仍然需要参数化函数中的惰性值,而且您不希望这些值是参数化夹具的变体。对于这些情况,现在也可以在pytest-cases中使用lazy_value解决方案了。使用它,您可以在参数值中使用函数,并且这些函数只有在执行手头的测试时才会被调用。
下面是一个示例,展示了两种编码风格(将use_partial布尔arg切换到True以启用另一种选择)
from functools import partial
from random import random
import pytest
from pytest_cases import lazy_value
database = [random() for i in range(10)]
def get_param(i):
return database[i]
def make_param_getter(i, use_partial=False):
if use_partial:
return partial(get_param, i)
else:
def _get_param():
return database[i]
return _get_param
many_lazy_parameters = (make_param_getter(i) for i in range(10))
@pytest.mark.parametrize('a', [lazy_value(f) for f in many_lazy_parameters])
def test_foo(a):
print(a)注意,如果您希望自定义测试if,lazy_value也有一个id参数。默认情况是使用函数__name__,对部分函数的支持是在路上。
您可以用同样的方式将固定装置参数化,但请记住,您必须使用@fixture_plus而不是@pytest.fixture。详情请参见文档。
顺便说一下,我是pytest-cases的作者;)
发布于 2020-04-15 18:44:34
您可能会发现这个解决方法很有用:
from datetime import datetime, timedelta
from time import sleep
import pytest
@pytest.mark.parametrize(
'lazy_params',
[
lambda: (datetime.now() - timedelta(days=1), datetime.now()),
lambda: (datetime.now(), datetime.now() + timedelta(days=1)),
],
)
def test_it(lazy_params):
yesterday, today = lazy_params()
print(f'\n{yesterday}\n{today}')
sleep(1)
assert yesterday < today样本输出:
========================================================================= test session starts ==========================================================================
platform darwin -- Python 3.7.7, pytest-5.3.5, py-1.8.1, pluggy-0.13.1 -- /usr/local/opt/python/bin/python3.7
cachedir: .pytest_cache
rootdir: /Users/apizarro/tmp
collected 2 items
test_that.py::test_it[<lambda>0]
2020-04-14 18:34:08.700531
2020-04-15 18:34:08.700550
PASSED
test_that.py::test_it[<lambda>1]
2020-04-15 18:34:09.702914
2020-04-16 18:34:09.702919
PASSED
========================================================================== 2 passed in 2.02s ===========================================================================https://stackoverflow.com/questions/54163532
复制相似问题