四 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'
test_tmpdir.py

 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'
test_tmdir.py

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
conftest.py
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
test_authors.py

 

二 使用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'))
test_config.py

上述例子展示可以使用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())
test_slow.py

 

 

四 使用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
test_capsys.py

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>

 

  

 

posted on 2020-04-11 13:32  rwwh  阅读(233)  评论(0)    收藏  举报

导航