一、Pytest
pytest 是 python 的一种单元测试框架,同自带的unittest测试框架类似。相比起unittest框架使用起来更简洁,效率更高。
Pytest特点:
1、文档丰富,文档中有很多实例可以参考
2、支持简单的单元测试和复杂的功能测试
3、支持参数化
4、执行测试过程中可以将某些测试跳过,或者对某些预期失败的case标记成失败
5、支持重复执行失败的case
6、支持运行由Nose.Unittest 编写的测试Case
7、具有很多第三方插件,可以自定义扩展
8、方便和持续集成工具集成
二、Pytest基本操作
#单元测试:
#1、测试函数、类、方法能不能正常运行完成
import pytest
def test_1():
print("test_1+++++")
return 1*0
def test_2():
print("test_2++++++")
return 1/0
if __name__ == '__main__':
pytest.main(["-s","test_py2.py"])
对于直接执行pytest:
模块:
直接从测试目录或者当前目录中递归寻找名称规则为test_*.py 或 *_test.py.
类:
以上模块中的Test开头的类,且没有初始化 __init__ 方法
三、Pytest 配置文件
[pytest]
addopts = -s
testpaths = ./
python_files = test_*.py *test.py
python_classes = Test*
python_functions = test_*
四、Pytest 标记跳过测试
两种标记装饰器:
@pytest.mark.skip(reason=None)
作为装饰器使用,添加到需要跳过的函数或者方法上;
@pytest.mark.skipif(condition,reason=None)
作为装饰器使用,添加到需要跳过的函数或者方法上,但只有条件成立时才会跳过
import pytest
def test_a():
print("test_a+++++++")
return 1+1
@pytest.mark.skip(reason="我想跳过")
def test_b():
print("test_b++++++")
return 1/0
if __name__ == '__main__':
pytest.main(["-s","test_py4.py"])
@pytest.mark.xfail(condition = None,reason = None,raises = None)
import pytest
def test_a():
print("test_a+++++++")
return 1+1
@pytest.mark.skip(reason="我想跳过")
def test_b():
print("test_b++++++")
return 1/0
@pytest.mark.xfile(reason = ZeroDivisionError)
if __name__ == '__main__':
pytest.main(["-s","test_py4.py"])
五、Pytest 参数化
对于相似的过程,但数据不一样的时候,可以使用参数化。
@pytest.mark.parametrize(self.argnames,argvalues,ids = None)
argnames 参数名称,列表或元组
argvalues 参数值,列表套元组
ids 测试id,可省略
import pytest
@pytest.mark.parametrize(["a","b"],[(1,2),(10,20),(30,40),(50,60),(70,80),(90,100),(110,120),(130,140)])
def test_a(a,b):
print("test_a+++++++")
assert a + b > 100
if __name__ == '__main__':
pytest.main(["-s", "test_py5.py"])
#3 failed, 5 passed
六、Pytest 夹具
作用:在测试之前和之后执行,用于固定测试环境,及清理回收测试资源。
模块级
def setup_module(module):
pass
def teardown_module(module):
pass
import pytest
def setup_module(args):
print("setup_module",args)
def teardown_module(args):
print("teardown_module",args)
def test_func_a():
print('----',"test_func_a")
def test_func_b():
print('----',"test_func_b")
class TestOne():
TAG = "a"
def test_1(self):
print('----', self.TAG + 'test_1')
def test_2(self):
print('----', self.TAG + 'test_2')
if __name__ == '__main__':
pytest.main(["-s","test_py6.py"])
函数级
import pytest
def setup_module(args):
print("setup_module",args)
def teardown_module(args):
print("teardown_module",args)
def setup_function(args):
print("setup_function",args)
def teardown_function(args):
print("teardown_function",args)
def test_func_a():
print('----',"test_func_a")
def test_func_b():
print('----',"test_func_b")
class TestOne():
TAG = "a"
def test_1(self):
print('----', self.TAG + 'test_1')
def test_2(self):
print('----', self.TAG + 'test_2')
if __name__ == '__main__':
pytest.main(["-s","test_py6.py"])
类级别
import pytest
def setup_module(args):
print("setup_module",args)
def teardown_module(args):
print("teardown_module",args)
def setup_function(args):
print("setup_function",args)
def teardown_function(args):
print("teardown_function",args)
def test_func_a():
print('----',"test_func_a")
def test_func_b():
print('----',"test_func_b")
class TestOne():
TAG = "a"
def setup_class(self):
print(" "+self.TAG + "setup_class")
def teardown_class(self):
print(" "+self.TAG + "teardown_class")
def test_1(self):
print('----', self.TAG + 'test_1')
def test_2(self):
print('----', self.TAG + 'test_2')
if __name__ == '__main__':
pytest.main(["-s","test_py6.py"])
方法级别
import pytest
def setup_module(args):
print("setup_module",args)
def teardown_module(args):
print("teardown_module",args)
def setup_function(args):
print("setup_function",args)
def teardown_function(args):
print("teardown_function",args)
def test_func_a():
print('----',"test_func_a")
def test_func_b():
print('----',"test_func_b")
class TestOne():
TAG = "a"
def setup_class(self):
print(" "+self.TAG + "setup_class")
def teardown_class(self):
print(" "+self.TAG + "teardown_class")
def setup_method(self,args):
print(" "+self.TAG + "setup_method",args)
def teardown_method(self,args):
print(" "+self.TAG + "teardown_method",args)
def test_1(self):
print('----', self.TAG + 'test_1')
def test_2(self):
print('----', self.TAG + 'test_2')
if __name__ == '__main__':
pytest.main(["-s","test_py6.py"])
七、Pytest 装饰器夹具
除了setup 和 teardown, pytest 提供 fixture 进行更为强大的夹具使用。
import pytest
@pytest.fixture()
def before():
print("before")
@pytest.mark.usefixtures("before")
def test_1():
print('test_1()')
if __name__ == '__main__':
pytest.main(["-s","test_py7.py"])
有返回值
import pytest
@pytest.fixture()
def before():
print("before")
@pytest.fixture()
def login():
print("login")
return "user"
@pytest.mark.usefixtures("before")
def test_1():
print('test_1()')
def test_2(login):
print('test2()',login)
if __name__ == '__main__':
pytest.main(["-s","test_py7.py"])
有参数
import pytest
@pytest.fixture()
def before():
print("before")
@pytest.fixture()
def login():
print("login")
return "user"
@pytest.mark.usefixtures("before")
def test_1():
print('test_1()')
def test_2(login):
print('test2()',login)
@pytest.fixture(params = [1,2,3])
def init_data(request):
print("request参数是",request.param)
return request.param
def test_Data(init_data):
assert init_data >2
if __name__ == '__main__':
pytest.main(["-s","test_py7.py"])
八、Pytest 插件
html 报告
[pytest]
addopts = -s --html=./report.html
testpaths = ./
python_files = test_*.py *test.py
python_classes = Test*
python_functions = test_*
指定执行顺序
import pytest
def test_1():
print("1")
def test_2():
print("2")
def test_3():
print("3")
@pytest.mark.run(order = 3)
def test_4():
print("4")
@pytest.mark.run(order = 2)
def test_5():
print("5")
@pytest.mark.run(order = 1)
def test_6():
print("6")
if __name__ == '__main__':
pytest.main(["-s","test_py9.py"])
注意:标了运行顺序的数越小(正整数和零),运行的优先级越高;标了运行顺序(正整数和零),运行的优先级大于没有标记的;运行顺序:零和正整数 > 没有标记 > 负整数;在各个阶段数越小的运行的优先级越高。