第一章 pytest入门
常用启动命令
1.pytest会在当前目录及子目录寻找测试用例
2.Task结构,用于UI层和API层之间传递信息:
from collections import namedtuple
Task = namedtuple('Task',['summary','owner','done','id'])
# __new__.__defaults__创建默认Task对象,不必指定所有属性
Task.__new__.__defaults__ = (None,None,False,None)
def test_defaluts():
t1=Task() # 默认值
t2=Task(None,None,False,None)
assert T1 == T2
# 演示使用属性名(而不是索引)来访问对象成员
def test_member_access():
t=Task('buy milke','brain')
assert t.summary == 'buy milke'
assert t.owner == 'brain'
assert (t.done,t.id) == (False,None)
# `_asdict` 和 `_replace`函数
3.运行单个测试用例: pytest -v test.py::test_asdict
4.常用命令行
--collect-only
: 只是展示给定配置下运行哪些测试用例,方便检查
-k
: 允许使用表达式指定运行测试方法
pytest -k "asdict or defaluts" --collect-only
pytest -k "asdict or defaluts"
pytest -v -k "asdict or defaluts"
-m
运行指定标记的用例
-m "mark1 and mark2"
同时标记mark1和mark2的
-m "mark1 or mark2"
标记mark1或mark2的
-m "mark1 and not mark2"
标记mark1,过滤掉mark2的
-x
遇到失败停止pytest运行
--maxfail=num
指定失败几次,停止运行 。num=1时同-x (指运行所有指定测试用例时,失败N次后停止)
-s 与 --capture=method
打印符合标准的输出或信息,如print
-l /--showlocals
测试失败时打印出局部变量名和值,可避免一些不必要的print
-v
或者 --verbose
: 可以查看具体运行详情
-q/--quiet
:与-v
相反,简化输出信息,常和--tb=line
(仅打印异常的代码位置)搭配使用
--tb=style
决定失败时输出信息的显示方式 (short,line,no:关闭,lang等)
第二章 编写测试函数
演示如何使用assert,如何处理可预期和不可预期的异常,如果借助类,模块,目录来组织测试,以便管理大量的测试。使用mark为预期会失败的测试做标记
测试示例程序
- test/func/init.py和test/unit/init.py都是空文件,作用是给pytest提供搜索路径,找到测试根目录和
pytest.ini
文件。(conftest.py也可以统一放在test根目录) -
pytest.ini
是可选的,它保存了pytest在该项目下的特定配置。 -
conftest.py
是pytest的“本地插件库”,包含hook函数和fixture。hook可以将自定义逻辑引入pytest,用于改善pytest的执行流程,fixture则是用于测试前后执行配置及销毁逻辑的外壳函数,可以传递测试中用到的资源。 - 测试demo下载 传送门
安装cd code/tasks_proj pip install .
使用assert
- pytest.org上有很多复杂的assert例子
- 预期异常
with pytest.raises(TypeError):
task.add(task='not a task object')
# 无论with内容是什么,都会发生TypeError异常。如果测试通过,说明发生了预期的TypeError异常;如果抛出其他类型的异常,则与预期不一致,测试失败。
with pytest.raises(ValueError) as excinfo:
tasks.start_task_db('some','mysql')
exception_msg=excinfo.value.args[0]
assert exception_msg == "db_type must be a 'tiny' or 'mongo'"
# 不止校验异常类型,还可以校验异常消息
标记测试
-
@pytest.mark.skip()
装饰器 : 跳过测试 (skipif
可设置跳过条件) -
@pytest.mark.xfail()
:告诉pytest运行此测试,但我们预期会失败(如果实际失败,则记过为xfail,如果实际成功,则结果为XPASS,可在pytest.ini中强制执行结果为fail
[pytest]
xfail_strict=ture
参数化
1.@pytest.mark.parametrize()
第一个参数为逗号分隔的字符串列表,第二个参数是值列表parametrize()
2.parametrize()在测试运行会优化测试用例可视化,也可引入额外自定义参数。ids
:标识用例的id
@pytest.mark.parametrize('a,b', [['2', '3'], ['1', '4']], ids=['2和3', '1和4'])
def test_param(a, b):
print("===", int(a))
assert int(a) + int(b) == 5
3.parametrize()可以加在测试类
上,这样数据集会被传递给所有类方法
4.parametrize()可以在参数值旁定义id做标识,pytest.param(<value>,id='something')
在id不能被参数化批量生成时,需要自定义时,很管用
param = [
pytest.param(1, 2, 5, id='failed'),
pytest.param(3, 4, 7, id='success')]
@pytest.mark.parametrize('a,b,expect', param)
def test_param2(a, b, expect):
assert int(a) + int(b) == int(expect)
第三章 pytest fixture
安装tasks,cd code;pip install ./tasks_proj/
1.fixture
函数会在测试函数之前运行,如果fixture函数包含yield,那么系统会在yield
处停止,转而运行测试函数,等测试函数运行完再回到fixture,运行yield后的代码
2.--setup-show
回溯fixture的执行过程
3.fixture内发生的assert异常结果为ERROR
,测试函数assert发生的异常结果为FAIL
4.usefixtures
5.autouse=True
作用域内的函数都运行该fixture
fixture作用范围scope
:
function
fixture默认值,每个测试函数运行一次,配置代码再测试用例运行之前运行,销毁代码再测试用例运行之后运行
class
每个测试类运行一次,无论测试类里有多少类方法都可以共享这个fixture
module
每个模块只需要运行一次,无论模块里有多少个测试函数,类方法或其他fixture都可以共享这个fixture
session
每次会话只需要运行一次,一次pytest会话中的所有测试函数,方法都可以共享这个fixture
--setup-show 可以观察每个fixture被调用的次数,以及在各自作用范围下执行配置,销毁逻辑的顺序
fixture只能使用同级别的或者更高级别的fixture,不能反过来
fixture参数化
@pytest.fixture(params=tasks,ids=task_ids) #也可参数化,也可指定id
def a_task(request): # request是pytest内建的fixture之一,代表fixture的调用状态,有一个param字段,会被params列表的一个元素填充
return request.param
2.fixture_value
内置fixture?
- 对测试函数进行参数化处理,可以多次运行的只是该测试函数;而使用参数化fixture,每个使用该fixture的测试函数都可以被运行多次,这一特性非常强大。
第四章 内置fixture
tmpdir和tmpdir_factory
内置的tmpdir
和tmpdir_factory
负责在测试开始运行前创建临时文件或目录,并在测试结束后删除。单个测试使用tmpdir
,多个测试使用tmpdir_factory
tmpdir
的作用范围是函数级别,tmpdir_factory
的是会话级别 ,如果需要其他级别的,需要创建一个新的fixture。
def test_tmpdir(tmpdir):
a_file=tmpdir.join("a.txt")
b_dir=tmpdir.mkdir("something")
b_file=b_dir.join("b.txt")
a_file.write("aaa")
b_file.write("bbb")
assert a_file.read()=="aaa" and b_file.read()=="bbb"
pytestconfig
内置的pytestconfig可以通过命令行参数,选项,配置文件,插件,运行目录等方式来控制pytest。它是request.config的快捷方式,被称为“pytest配置对象”
def pytest_addoption(parser): #pytest的hook函数pytest_addoption,可添加命令行选项
parser.addoption("--myopt",action="store_true",help="some my option")
parser.addoption("--foo",action="store",default="bar",help="foo:bar or baz")
def test_option(pytestconfig):
print(pytestconfig.gettoption('myopt'))
print(pytestconfig.gettoption('foo'))
print(pytestconfig.option.foo)
# 一些例子
def test_pytestconfig(pytestconfig):
print(pytestconfig.args)
print(pytestconfig.inifile)
print(pytestconfig.invocation_dir)
print(pytestconfig.rootdir)
print(pytestconfig.getoption('showlocals'))
使用cache
cache用于测试会话传递给下一段会话
--last-failed(仅运行上次未通过的)和--failed-first (之前未通过的首先运行)很好的展示cache的功能,看看cache是如何存储这些标识数据的。
--cache-show
可以显示cache存储的信息
--clear-cache
运行前清空cache缓存
def test_cache(cache):
cache.get(key,default)
cache.set(key,value)
使用capsys
允许使用代码读取stdout和stderr,capsys.redouterr()
也可以临时禁止抓取日志输出capsys.redouterr()
。
pytest通常会抓取输出,仅当全部用例结束后,抓取到的日志才会显示出来。--s
参数可以关闭这个功能,在测试运行期就把输出直接发送到stdout,但有时可能只需要部分信息,则可以用 capsys.disabled()
临时让输出绕过默认的输出捕获机制
capsys.redouterr()
def test_disabled(capsys):
with.capsys.disabled():
print("aaaa")
print("bbb") # pytest -q 和 pytest -q -s的区别
monkeypatch
在运行期间对类或模块进行动态修改。常用于替换被测试代码的部分运行环境,或将输入依赖或输出依赖替换成更容易测试的对象或函数。测试结束后,无论结果是通过还是失败,代码都会复原(所有修改都会撤销)
例如:修改环境变量monkey.setenv('HOME',tmpdir.mkdir('home'))
其他函数(setattr )