pytest介绍
pytest整合了unittest,包含了unittest中的方法,并且比其更轻便快捷,而且结合Allure报告能够清晰的生成测试报告。 1、安装: pip install pytest 2、测试文件: test_*.py _test.py 3用例识别: Test类包含的所有的test_*的方法(测试类不能带有init方法) 不在class中的所有test_*方法 pytest也可以执行unittestu昂加写的用例和方法
终端执行
pytest 文件名 pytest py_test.py 参数: -v -s ::类名 ::类名::方法名
pytest py_test.py::Testdemo2
-k pytest -k 文件名 “类名 and not 方法名” —跳过
pytest py_test.py -k "TestDemo1 and not
test_one"@skip() pytest -m [标记名] @pytest.mark[标记名] pytest -x 文件名 —一旦运行到报错就停止运行 pytest --maxfail=[num] 错误数目,如果错误数目达到
pytest执行-失败重新运行
场景: 1、测试过程中,可能有弹框、网络错误导致加载失败,可以通过失败重新执行来做 2、安装: pip install pytest-rerunsfailuers 3、执行: pytest --reruns 3 -v -s test_class.py pytest -v --ruruns 5 -reruns-delay 1#增加时间间隔
安装断言所需要的库 pip install pytest-assume pytest.assume(判断表达式) pytest py_test.py::TestDemo2::test_two
执行方法: pychrm中配置为pytest mian方法中:pytest.main("-v -x TestDemo")
pytest的架构
优先级:(高->低) setup_module:在所有的方法执行之前执行 setup_class:在这个类的执行之前执行 setup_function:类外部方法 setup_method: setup
pytest-fixture
fixture 的功能 主要包括以下三点: 传入测试中的数据集 配置测试前系统的初始状态, 为批量测试提供数据源
pytest-fixture的用法
fixture用来解决很多测试方法的前置条件不一样,无法用setup解决这种不同的时候,使用 @pytest.fixture 加在我们所需的前置条件的方法上,以参数名的形式传给所需要的测试方法 fixture可以用作,参数中可以传入方法
class TestDemo1:
@pytest.fixture()
def test_login(self):
print("这是一个登陆方法")
def test_one(self,test_login):
print("1需要登录 test_one")
def test_two(self):
pytest.assume( 1 == 2)
print("test_two")
def test_three(self,test_login):
pytest.assume( 1 == 2)
print("3需要登录test_three")
'''
结果:在每个添加前置方法的测试方法前,执行了这个方法
py_test.py::TestDemo1::test_one 这是一个登陆方法
1需要登录 test_one
PASSED
py_test.py::TestDemo1::test_two test_two
FAILED
py_test.py::TestDemo1::test_three 这是一个登陆方法
3需要登录test_three
FAILED
'''
fixture()函数中的参数
scope作用域
应用的范围默认为function,类似于setup和teardown中作用域的方法
fixture的自动应用
就是让我们标记的前置方法以及后置方法,不通过测试方法中添加方法函数名,而是自动应用到这个模块下的所有方法中:
class Test_fixtures:
@pytest.fixture(scope="function", autouse=True) # 只调用一次
def open(self):
print("打开浏览器")
yield # 之前的操作,相当于setup,之后的操作,相当于teardown的操作
print("执行teardown")
print("最后关闭浏览器")
def test_search1(self):
print("test_search1")
#raise NameError
def test_search2(self):
print("test_search2")
if __name__ == '__main__':
pytest.main("-v -s")
'''
结果:
test_fixture.py 打开浏览器
.test_search1
执行teardown
最后关闭浏览器
打开浏览器
.test_search2
执行teardown
最后关闭浏览器'''
数据共享文件conftest
那么我们的login方法在开发时其实需要放在一个公共的地方,哪里需要,放在哪里,其实可以将他从这个模块中拿出来,放在一个公共的地方:首选conftest.py文件 conftest.py文件非常重要,是pytest提供的特殊的进行数据共享的文件,执行到login方法时时,先从本模块中找,如果则执行,没有则找该文件,有则执行,无则报错。 注意: 1、conftest文件名不能更换 2、conftest要和执行的文件放在同一个package下
import pytest
@pytest.fixture(scope="module")#只调用一次
def open():
print("打开浏览器")
yield
#之前的操作,相当于setup,之后的操作,相当于teardown的操作
print("执行teardown")
print("最后关闭浏览器")
def test_search1(open):
print("test_search1")
raise NameError
pass
def test_search2(open):
print("test_search2")
pass
if __name__ == '__main__':
pytest.main("-v -s")
pytest测试数据参数化
先来看下params的定义
:param params:
An optional list of parameters which will cause multiple invocations
of the fixture function and all of the tests using it. The current
parameter is available in ``request.param``.
参数必须使用request.param返回
import pytest
@pytest.fixture(params=("one","two"), scope='function', autouse=False)
def search1(request):
print("test_search1")
print(type(request.param))
return request.param
def test_search1(search1):
print(f"{search1}")
'''
test_fixture01.py test_search1
<class 'str'>
.one
test_search1
<class 'str'>
.two
'''
传参的方式可以使用@pytest.mark.parameterize(“参数名”,data) 这种方式使用起来更加灵活 举个栗子:判断输入与期望值是否一致
@pytest.mark.parametrize("test_input,expected",[("2+1",3),("2+9",11),("2+5",9)])
def test_search1(test_input,expected):
#eval()将字符串转化为数值的表达式
pytest.assume(eval(test_input) == expected)
上面的方法是将所有的组合罗列出来,下面这个方法是将单个参数的值罗列出来,会自己去组合,效果和自己组合的是一样的
@pytest.mark.parametrize('test_input',["2+1","2+9"])
@pytest.mark.parametrize('expected',[3,8])
#@pytest.mark.parametrize("test_input,expected",[("2+1",3),("2+9",11),("2+5",9)])
def test_search1(test_input,expected):
#eval()将字符串转化为数值的表达式
pytest.assume(eval(test_input) == expected)
pytest将方法作为参数
测试方法中,参数为方法名,indirect=True时,将参数作为该方法的返回值,传入测试方法中
import pytest
#方法名作为参数
data = {'user1','user2'}
@pytest.fixture(scope='module')
def login_r(request):
#接收传入的参数
user = request.param
print(f'\n打开登录页准备登录,登录用户:{user}')
return user
#这里将login_r作为测试方法的参数,主要是参数indirect=True起作用,默认情况是不把方法名作为一个方法的
@pytest.mark.parametrize('login_r',data,indirect=True)
def test_login_r(login_r):
a = login_r
print(f'测试用例的返回值为:{a}')
assert a != ""
pytest跳过某一部分测试用例
跳过某条用例 @pytest.mark.skip(“此次不执行这条用例先跳过”,reason=“不执行原因是”) 2、特定的判断条件下不执行这条用例,并写明原因: @pytest.mark.skip(sys.plantform == 2.5,reason=“不执行原因是”) 3、不跳过加标注的方式以pass的结果不执行某条用例 @pytest.mark.xfail 总结:
使用自定义标记mark只执行某部分的测试用例
import pytest
#将模块归类
@pytest.mark.search
def test_search1():
print("test_search1")
raise NameError
pass
@pytest.mark.search
def test_search2():
print("test_search2")
pass
def test_search3():
print("test_search3")
pass
@pytest.mark.login
def test_login1():
print("test_login1")
pass
@pytest.mark.login
def test_login2():
print("test_login2")
pass
terminal中输入 pytest -s pytest -s q.py -n 4
|