系列分享
重命名
固件的名称默认为定义时的函数名,如果不想使用默认,可以通过 name 选项指定名称:
@pytest.fixture(name='name')
def calculate_average():
return 28
def test_age(name):
assert name == 28
if __name__ == '__main__':
pytest.main()
执行结果
固件参数化
在之前我们介绍了参数化,固件也是函数,我们也可以对固件进行参数化,
假设现在有一批 API 需要测试对不同数据库的支持情况(对所有数据库进行相同操作),最简单的方法就是针对每个数据库编写一个测试用例,但这包含大量重复代码,如数据库的连接、关闭,查询等。
使用固件抽离出数据库的通用操作,每个 API 都能复用这些数据库固件,同时可维护性也得到提升。
import pytest
@pytest.fixture(params=[
('redis', '6379'),
('elasticsearch', '9200')
])
def param(request):
return request.param
@pytest.fixture(autouse=True)
def db(param):
print('\nSucceed to connect %s:%s' % param)
yield
print('\nSucceed to close %s:%s' % param)
def test_api():
assert 1 == 1
执行结果
与函数参数化使用 @pytest.mark.parametrize 不同,固件在定义时使用 params 参数进行参数化。
固件参数化依赖于内置固件 request 及其属性 param。
内置固件
tmpdir & tmpdir_factory
用于临时文件和目录管理,默认会在测试结束时删除。
tmpdir 只有 function 作用域,只能在函数内使用。
使用 tmpdir.mkdir() 创建目临时录,tmpdir.join() 创建临时文件(或者使用创建的目录)
def test_tmpdir(tmpdir):
a_dir = tmpdir.mkdir('mpdir')
a_file = a_dir.join('file.txt')
a_file.write('hello, pytest!')
assert a_file.read() == 'hello, pytest!'
执行结果:
tmpdir_factory 可以在所有作用域使用,包括 function, class, module, session
@pytest.fixture(scope='module')
def my_tmpdir_factory(tmpdir_factory):
a_dir = tmpdir_factory.mktemp('mpdir')
a_file = a_dir.join('file.txt')
a_file.write('hello, pytest!')
return a_file
pytestconfig
使用 pytestconfig,可以很方便的读取命令行参数和配置文件。
下面示例演示命令行参数解析:首先在 conftest.py 中使用函数 pytest_addoption (特定的 hook function ):
def pytest_addoption(parser):
parser.addoption('--host', action='store',
help='host of db')
parser.addoption('--port', action='store', default='8888',
help='port of db')
然后就可以在测试函数中通过 pytestconfig 获取命令行参数:
def test_option1(pytestconfig):
print('host: %s' % pytestconfig.getoption('host'))
print('port: %s' % pytestconfig.getoption('port'))
pytestconfig 其实是 request.config 的快捷方式,所以也可以自定义固件实现命令行参数读取。
# conftest.py
def pytest_addoption(parser):
parser.addoption('--host', action='store',
help='host of db')
parser.addoption('--port', action='store', default='8888',
help='port of db')
@pytest.fixture
def config(request):
return request.config
# test_config.py
def test_option2(config):
print('host: %s' % config.getoption('host'))
print('port: %s' % config.getoption('port'))
执行:
pytest test_11.py --host=123 -s
结果:
capsys
capsys 用于捕获 stdout 和 stderr 的内容,并临时关闭系统输出
def ping(output):
print('Pong...', file=output)
def test_stdout(capsys):
ping(sys.stdout)
out, err = capsys.readouterr()
assert out == 'Pong...\n'
assert err == ''
def test_stderr(capsys):
ping(sys.stderr)
out, err = capsys.readouterr()
assert out == ''
assert err == 'Pong...\n'
monkeypatch
from SomeOtherProduct.SomeModule import SomeClass
def speak(self):
return "ook ook eee eee eee!"
SomeClass.speak = speak
ytest 内置 monkeypatch 提供的函数有:
setattr(target, name, value, raising=True),设置属性;
delattr(target, name, raising=True),删除属性;
setitem(dic, name, value),字典添加元素;
delitem(dic, name, raising=True),字典删除元素;
setenv(name, value, prepend=None),设置环境变量;
delenv(name, raising=True),删除环境变量;
syspath_prepend(path),添加系统路径;
chdir(path),切换目录。
其中 raising 用于通知 pytest 在元素不存在时是否抛出异常;prepend 如果设置,环境变量将变为 value+prepend+<old value> 。
测试过程中修改了环境变量:
def test_config_monkeypatch(tmpdir, monkeypatch):
monkeypatch.setenv('HOME', tmpdir.mkdir('home'))
dump_config(config)
path = os.path.expanduser('~/.conf.json')
expected = json.load(open(path, 'r', encoding='utf-8'))
assert expected == config
recwarn
recwarn 用于捕获程序中 warnings 产生的警告。
def warn():
warnings.warn('Deprecated function', DeprecationWarning)
def test_warn(recwarn):
warn()
assert len(recwarn) == 1
w = recwarn.pop()
assert w.category == DeprecationWarning
也可以使用 pytest.warns() 捕获警告
def test_warn2():
with pytest.warns(None) as warnings:
warn()
assert len(warnings) == 1
w = warnings.pop()
assert w.category == DeprecationWarning