四 pytest内置的Fixture
一 使用tmpdir和tmpdir_factory
内置的tmpdir和tmpdir_factory负责在测试开始运行前创建临时文件目录,并在测试结束后删除,tmpdir的作用范围是函数级别的,其作用是返回一个唯一的临时目录对象(py.path.local)
def test_tmpdir(tmpdir): a_file = tmpdir.join('something.txt') a_sub_dir = tmpdir.mkdir('anyting') another_file = a_sub_dir.join('something_else.txt') a_file.write('contents may settle during shipping') another_file.write('something different') assert a_file.read() == 'contents may settle during shipping' assert another_file.read() == 'something different'
tmpdir_factory的作用范围是会话级别的,它与tmpdor很像,它们有不同的接口
def test_tmpdir_factory(tmpdir_factory): #创建一个名为mydir的目录 a_dir = tmpdir_factory.mktemp('mydir') #getbasetemp()函数返回了该会话使用的根目录 base_temp = tmpdir_factory.getbasetemp() print('base:',base_temp) a_file = a_dir.join('something.txt') a_sub_dir = a_dir.mkdir('anyting') another_file = a_sub_dir.join('something_else.txt') a_file.write('contents may settle during shipping') another_file.write('something different') assert a_file.read() == 'contents may settle during shipping' assert another_file.read() == 'something different'
pytest -NUM会随着会话的递增而递增.pytest会记录最近几次会话使用的根目录,更早的更目录记录会被清理掉

注:也可以使用pytest -basetemp=mydir指定自己的根目录.
在其他作用范围内使用临时目录
tmpdir的作用范围是函数级别的,tmpdir的作用范围是函数级别的,若需要模块或类级别作用范围的目录,可以利用tmpdor_factory再创建一个fixture.
示例:假设有一个测试模块,其中有很多测试用例要读取一个JSON文件.可以在模块本身或conftest.py中创建一个作用范围四模块级别的fixture如下所示:
import pytest from tasks import Task import tasks import json @pytest.fixture(scope='module') def author_file_json(tmpdir_factory): python_author_data = { 'Ned': {'City': 'Boston'}, 'Brian': {'City': 'Portland'}, 'Luciano': {'City': 'Sau Paulo'} } file = tmpdir_factory.mktemp('data').join('author_file.json') print('file:{}'.format(str(file))) with file.open('w') as f: json.dump(python_author_data,f) return file
import json #两个测试用例可以用同一个数据文件 def test_brian_in_portland(author_file_json): """A test that uses a data file.""" with author_file_json.open() as f: authors = json.load(f) assert authors['Brian']['City'] == 'Portland' def test_all_have_cities(author_file_json): """Same file is used for both tests.""" with author_file_json.open() as f: authors = json.load(f) for a in authors: assert len(authors[a]['City']) > 0
二 使用pytestconfig
pytestconfig 可以通过命令行参数、选项、配置文件、插件、运行目录等方式来控制pytest;
pytestconfig 实际上就是 request.config 的快捷方式,被称为“pytest 配置对象”;
下面使用pytest的hock函数pytest_addoption添加几个命令行选项:
def pytest_addoption(parser): parser.addoption("--myopt",action='store_false',help='some boolean option') parser.addoption("--foo", action="store", default="bar", help="foo: bar or baz")
使用pytest -h,可以看到自定义的命令行选项

接下来可以使用命令行这些选项了:
import pytest #因为pytestconfig是一个fixture,所以参数列表中可以直接使用pytestconfig,或者被其他fixture使用 def test_option(pytestconfig): print('"foo" set to',pytestconfig.getoption('foo')) print('"myopt" set to',pytestconfig.getoption('myopt'))

import pytest #因为pytestconfig是一个fixture,所以参数列表中可以直接使用pytestconfig,或者被其他fixture使用 def test_option(pytestconfig): print('"foo" set to',pytestconfig.getoption('foo')) print('"myopt" set to',pytestconfig.getoption('myopt')) @pytest.fixture() def foo(pytestconfig): return pytestconfig.option.foo @pytest.fixture() def myopt(pytestconfig): return pytestconfig.option.myopt def test_fixtures_for_options(foo, myopt): print('"foo" set to:', foo) print('"myopt" set to:', myopt) #可以调用内置的选项 def test_pytestconfig(pytestconfig): print('args :', pytestconfig.args) print('inifile :', pytestconfig.inifile) print('invocation_dir :', pytestconfig.invocation_dir) print('rootdir :', pytestconfig.rootdir) print('-k EXPRESSION :', pytestconfig.getoption('keyword')) print('-v, --verbose :', pytestconfig.getoption('verbose')) print('-q, --quiet :', pytestconfig.getoption('quiet')) print('-l, --showlocals:', pytestconfig.getoption('showlocals')) print('--tb=style :', pytestconfig.getoption('tbstyle')) def test_legacy(request): print('\n"foo" set to:', request.config.getoption('foo')) print('"myopt" set to:', request.config.getoption('myopt')) print('"keyword" set to:', request.config.getoption('keyword'))
上述例子展示可以使用pytestconfig调用内置的选项,以及那些pytest启动时的信息(目录,参数等)
三 使用cache
cache的作用是存储一段测试会话信息,在下一段测试会话中使用.使用pytest内置的--last-failed和--failed-first标识可以很好地展示cache的功能.
可以使用pytest --cache-show来显示存储的信息,也可以在测试会话开始前传入--clear-cache标识来清空缓存
cache接口
cache.get(key,default)
cache.set(key,value)
示例:
import datetime import pytest import random import time #创建一个fixture,记录测试的耗时.并存储到cache,若接下来的测试耗时大于之前的两倍,就抛出异常 @pytest.fixture(autouse=True) def check_duration(request,cache): key = 'duration/' + request.node.nodeid.replace(':','_') start_time =datetime.datetime.now() yield stop_time = datetime.datetime.now() this_duration = (stop_time - start_time).total_seconds() last_furation = cache.get(key,None) cache.set(key,this_duration) if last_furation is not None: errorstring = "test duration over 2x last duration" assert this_duration <= last_furation * 2,errorstring # @pytest.mark.parametrize('i',range(5)) def test_slow_stuff(i): time.sleep(random.random())

四 使用capsys
pytest内置capsys有两个功能:允许使用代码读取stdouthestderr;可以临时禁止抓取日志输出
import sys import pytest import random def greeting(name): print('Hi,{}'.format(name)) def test_greeting(capsys): greeting('AA') out,err = capsys.readouterr() assert out == 'Hi,AA\n' assert err == '' greeting('BB') greeting('CC') out, err = capsys.readouterr() assert out == 'Hi,BB\nHi,CC\n' assert err =='' def yikes(problem): print('YIKES! {}'.format(problem), file=sys.stderr) def test_yikes(capsys): yikes('Out of coffee!') out, err = capsys.readouterr() assert out == '' assert 'Out of coffee!' in err
pytest通常会抓取测试用例及测试代码的输出.仅当全部测试会话运行结束后,抓取到的输出才会随着失败的测试显示出来.--s参数可以关闭这个功能,在测试仍在运行期间就把输出直接发送到stdout,但是有时可能又需要其中的部分信息.此时可以使用capsys,capsys.disabled()可以临时让输出绕过默认的输出捕获机制
def test_capsys_disabled(capsys): #每次都会显示'always print this'这条信息,是因为它是在含有capsys.disabled()的代码块中运行 with capsys.disabled(): print('\nalways print this') #只有传入-s标识才会显示.-s标识是--capture=no的简写,表示关闭输出捕获 print('normal print, usually captured')

五 使用monkeypatch
monkey patch可以在运行期间对类或模块进行动态修改.在测试中,monkey patch常用于替换被测试代码的部分运行环境,或者将输入依赖或输出依赖替换成更容易测试的对象或函数.测试结束后,无论结果是通过还是失败,代码都会复原(所有修改都会撤销)
#设置一个属性 setattr(target,name,value=<notset>,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) #将路径path加入sys.path并放在最前,sys.path是python导入的系统路径列表 syspath_prepend(path) #改变当前的工作目录 chdir(path) 注;raising参数用于指示pytest是否在记录不存在时抛出异常 setenv()函数里的prepend参数可以是一个字符,如果这样设置的话,那么环境变量的值就是value+prepend+<old value>
浙公网安备 33010602011771号