介绍
pytest是python的单元测试框架,类似于python自带的unittest测试框架。
相比unittest使用简洁、效率更高。
pytest和unittest区别
1、pytest是基于unittest衍生出来的新的测试框架,使用起来相对于unittest来说更简单、效率来说更高,pytest兼容unittest测试用例,但是反过来unittest不兼容pytest
2、pytest的断言比unittest要简单些,unittest断言需要记很多断言格式,pytest只有assert一个表达式,用起来比较方便
3、pytest的前置后置比unittest多, 而且可以自定义条件
4、.unittest参数化可以通过nose_parameterized来实现,如@nose_parameterized.parameterized.expand(data),
‘data’为list格式的参数化的数据
而pytest参数化
通过装饰器@pytest.mark.parametrize来实现
5、pytest支持很多插件,比如失败重新执行、控制用例执行顺序、测试报告插件
6、unittest 通过HTMLTestRunner生成测试测试报告
pytest 通过pytest-html生成html格式报告,或者通过allure生成方案
pytest的特点
1、非常容易上手,入门简单,文档丰富,文档中很多实例可以参考
2、支持简单的单元测试和复杂的功能测试
3、支持参数化
4、执行测试过程中可以将某些测试跳过,或者对某些预期失败的case标记为失败
5、支持重复执行失败case
6、支持运行由unittest编写的case
7、具有很多第三方插件,并且可自定义扩展
8、方便和持续集成工具集成
安装
sudo pip install -U pytest
安装成功校验 : pytest --version
获取帮助信息
pytest -h
查看版本信息
pytest --version
pytest 示例
import pytest
# 不需要创建测试类,直接编写测试函数也ok
# 测试函数名 必须以test开头,或者以test结尾
def test_a():
print("aaa")
assert 1
def test_b():
print('bbb')
assert 0
if __name__ == '__main__':
pytest.main(['./test.py'])
setup和teardown函数
模块级别,对整个py文件起作用
setup_module / teardown_module
函数级别,对测试用例作用(不在测试类中)
setup_function / teardown_function
类级别,对测试类作用
setup_class/teardown_class
方法级别,对测试类中的测试用例作用(在测试类中)
setup_method / teardown_method
import pytest
def setup_module():
print("在模块开始执行前执行")
def teardown_module():
print("在模块运行完后执行")
def setup_function():
print("在函数执行前执行")
def teardown_function():
print("在函数执行后执行")
def test_c():
print("测试用例c")
assert 1
def test_d():
print("测试用例d")
assert 1
class TestCase():
def setup_class(self):
print("类开始执行前执行")
def teardown_class(self):
print("类执行完后执行")
def setup_method(self):
print("类方法开始执行前执行")
def teardown_method(self):
print("类方法开始执行前执行")
def test_e(self):
print("测试用例e")
assert 1
def test_f(self):
print("测试用例f")
assert 1
pytest.main(['test_b'])
测试类
测试类要符合特定的规则,pytest才能发现它:
1、测试类的命令要符合Test*规则;
2、测试类中不能有__init__()方法;
3、测试类中的方法和函数编写规则一致。
class TestClass:
def test_one(self):
x = 'this'
assert 'h' in x
def test_two(self):
x = 'hello'
assert hasattr(x, 'check')
pytest配置文件
pytest的配置文件通常放在测试目录下,名称为pytest.ini,命令运行时会使用配置文件中的配置。注意:pytest.ini文件中不能出现中文。
[pytest]
testpaths = ./
python_files=test_*.py
python_classes=Test*
python_functions=test_*
运行方式
1、在python代码中调用pytest
pytest.main(["-x", “mytestdir”])
2、执行case-指定模块
$ pytest test.py
3、 执行case-指定目录
$ pytest testing/
4、 执行case-文件名、类名或者函数名中包含特定关键字
pytest -k “_class and not two” .
5、执行case-指定nodeid
pytest为每一个收集到的测试用例指定一个唯一的nodeid。其由模块名加说明符构成,中间以::间隔。
说明符可以是类名、函数名以及由parametrize标记赋予的参数:
6、执行case-指定标记
pytest -m slow
7、执行case-指定包
pytest --pyargs pkg.testing
pytest会引入pkg.testing包,并在它的系统目录下搜寻测试用例并执行;
8、 执行多个case
a、执行当前目录及子目录下所有符合test_.py或者_test.py规则的文件。
$ pytest
b、执行指定的测试用例文件
$ pytest test_a.py test_b.py
9、 通过python -m pytest调用pytest
和pytest 运行效果相同
执行结束时返回的状态码
pytest命令执行结束,可能会返回以下六种状态码:
0:(OK)所有收集到的用例测试通过
1:(TESTS_FAILED)有用例测试失败
2:(INTERRUPTED)用户打断测试执行
3:(INTERNAL_ERROR)测试执行的过程中,发生内部错误
4:(USAGE_ERROR)pytest命令使用错误
5:(NO_TESTS_COLLECTED)没有收集到测试用例
它们在枚举类 _pytest.main.ExitCode 中声明。并且,其作为公开API的一部分,能够直接引入和访问:
from pytest import ExitCode
pytest 常用插件之控制用例执行顺序
默认情况下测试用例是从上到下执行的。可通过第三方插件改变case执行顺序。
插件名称:pytest-ordering
安装:pip install pytest-ordering
使用:1、在被测试函数上使用 @pytest.mark.run(order=x)
x 越小优先级越高
断点
1、import pdb
import pdb
在需要的地方加入:pdb.set_trace(),当其被调用时,pytest会停止这条用例的输出,其他用例不受影响。通过continue命令,退出PDB环境,并继续执行用例。
2、 使用内置的中断函数 breakpoint()
python 3.7 及以后支持: breakpoint()
3、开始执行时就加载PDB环境
pytest --trace
pytest允许在每个测试用例开始执行时,就加载PDB环境。
4、失败时加载PDB(Python Debugger)环境
a、PDB是python内建的诊断器,执行pytest --pdb 可进入诊断模式。
b、pytest会在测试用例失败(或者Ctrl+C)时,调用这个诊断器:
c、失败的信息存储在sys.last_value, sys.last_type, sys.last_traceback变量中
d、使用exit命令可退出诊断模式。
报告
1、总结报告
i、-r选项可以在执行结束后,打印一个简短的总结报告。在执行的测试用例很多时,可以让你对结果有个清晰的了解:
ii、-r选项后面要紧接这一个参数,用于过滤显示测试用例的结果。
以下是所有有效的字符参数:
f:失败的
E:出错的
s:跳过执行的
x:跳过执行,并标记为xfailed的
X:跳过执行,并标记为xpassed的
p:测试通过的
P:测试通过,并且有输出信息的;即用例中有print等
a:除了测试通过的,其他所有的;即除了p和P的
A:所有的
iii、上述字符参数可以叠加使用
2、 创建JUnitXML格式的测试报告
pytest --junitxml=path
可以在指定的path中创建一个能被Jenkins或者其他CI工具读取的XML格式的测试报告
3、 为测试报告提供URL链接 – pastebin服务
a、为每一个失败的测试用例创建一个URL
pytest --pastebin=failed
也可以通过添加-x选项,只为第一个失败的测试用例创建一个URL;
b、为所有的测试用例创建一个URL
pytest --pastebin=all
pytest 常用插件之测试报告
插件名称:pytest-html
安装:pip install pytest-html
使用:在配置文件中添加参数
addopts = --html=report/report.html
设置允许失败的用例数
达到上限后退出循环
pytest -x # 遇到第一个失败时,退出执行
pytest --maxfail==2 # 遇到第二个失败时,退出执行
pytest 常用插件之失败重试
插件名称:pytest-rerunfailures
安装:pip install pytest-rerunfailures
使用:在配置文件命令行添加新参数
失败重跑两次
addopts = --html=report/report.html --reruns=2
修改回溯信息的输出模式
1、pytest回溯信息的输出一共有六种模式:auto/long/short/line/native/no,用–tb选项指定:
pytest -l, --showlocals # 打印本地变量
pytest --tb=auto # 默认模式
pytest --tb=long # 尽可能详细的输出
pytest --tb=short # 更简短的输出
pytest --tb=line # 每个失败信息总结在一行中
pytest --tb=native # python的标准输出
pytest --tb=no # 不打印失败信息
2、–full-trace是一种比–tb=long更详细的输出模式。它甚至能观察到用户打断执行(Ctrl+C)时的回溯信息,而上述六种模式默认是不输出此类信息的。
分析测试执行时间
获取执行最慢的10个测试用例
pytest --durations=10
断言
1、使用python标准的assert表达式写断言
assert a == 0, “value was odd, should be even”
2、 编写触发期望异常的断言
import pytest
def myfunc():
raise ValueError("Exception 123 raised")
def test_match():
with pytest.raises(ValueError) as excinfo:
myfunc()
assert '123' in str(excinfo.value)
3、 为失败断言添加自定义的说明
a、重写__repr__()方法
class Foo:
def __init__(self, val):
self.val = val
def __repr__(self):
return str(self.val)
def test_foo_compare():
f1 = Foo(1)
f2 = Foo(2)
assert f1 == f2
b、重写pytest_assertrepr_compare()钩子函数
from .test_foo_compare import Foo
def pytest_assertrepr_compare(op, left, right):
if isinstance(left, Foo) and isinstance(right, Foo) and op == "==":
return [
"比较两个Foo实例:", # 顶头写概要
" 值: {} != {}".format(left.val, right.val), # 除了第一个行,其余都可以缩进
]
跳过测试函数
1、根据特定的条件,不执行标识的测试函数
skipif(condition, reason=None)
condition:跳过的条件,必传参数
reason:标注原因,必传参数
2、在测试用例前使用方法 @pytest.mark.skipif(2>1, reason=“跳过测试”)
标记为预期失败的函数
1、xfail(condition=None, reason=None,raises=None,run=True,strict=False)
condition:预期失败的条件。条件为真时预期失败
2、在测试用例前使用 @pytest.mark.xfail(condition, reason=‘XX’)
3、在配置文件中添加参数
xfail_strict=true ,预测失败但是成功的会算到失败的case里。
函数数据参数化
1、parametrize(argnames, argvalues)
argnames: 参数名
argvalues:参数值,类型列表格式
2、在测试函数前使用@pytest.mark.parametrize(“mobile”,[“13245452452”,“14425625263”])
3、多个参数,注册多个@pytest.mark.parametrize。
或者@pytest.mark.parametrize(“mobile,code”,[(“13245452452”,“120”),(“14425625263”,“121”)])
fixture
1、fixture相对于setup和teardown来说有几个优势
a.命名方式灵活,不局限于setup和teardown这几个命名
b.conftest.py配置里可以实现数据共享,不需要import就能自动找到一些配置。
c.scope = “module” 可以实现多个多个.py跨文件共享前置,每一个.py文件调用一次
d.scope=“session” 可实现多个.py跨文件使用一个session来完成多个用例。
e.fixture(scope=“function”, params=None, autouse=False,ids=None,name=None)
使用装饰器标记fixture功能。
autouse 是否自动启用
2、创建fixture
@pytest.fixture()
# 编写普通函数
def login():
print("登录获取token")
3、使用fixture
在需要使用fixture的测试用例中,把fixture函数的名词当参数传入即可。
def test_shopping(login):
print("测试购物")
使用yield 在一个fixture函数中实现teardown
import pytest
@pytest.fixture()
def login():
print("开始浏览器")
yield
print("关闭浏览器")
def test_shop(login):
print("购物")
if __name__ == '__main__':
pytest.main(['./test_a.py'])
addfinalizer使用
yield 当用例执行完后 ,会执行yield后面的代码,但是不能return
addfinalizer和yield一样,但是可以return参数,传给后面的测试用例
使用装饰器引用fixture
@pytest.fixture()
def before():
print(“开始执行”)
@pytest.mark.usefixtures(“before”)
def test_a():
pass
pytestconfig
pytestconfig 可以通过命令行参数、选项、配置文件、插件、运行目录等方式来控制pytest;
pytestconfig 实际上就是 request.config 的快捷方式,被称为“pytest 配置对象”;
pytest.config.getoption("–jenkins_job_url") 获取命令行参数
pytestconfig.getini(‘markers’) 获取 pytest.ini 配置文件中参数的值