·后期需要用pytest来开发自动化框架,开始重新整理pytest相关知识
PyTest
- 一、Pytest overview
- 二、Pytest的安装和规则约束
- 三、跑一个demo pytest脚本
- 四、用例前置和后置执行
- 五、pytest的自定义运行规则
- 六、pytest断言的使用
- 七、标记mark的使用
- 八、pytest数据参数化
- 九、pytest常用插件(测试报告、失败重跑)
一、Pytest overview
概览,包含编写规范、执行顺序、前后置方法函数、参数化、断言、测试报告的输出、失败重跑。
用例编写规范
- 测试文件名必须以"test_“开头或”_test"结尾
- 测试方法名必须以"test_开头"
- 测试类命名以"Test"开头
用例的分类执行
可以通过@pytest.mark来标记类和方法,pytest.main加入参数 -m 可以只运行标记的类和方法
pytest -v会执行所有
pytest -s test_name.py;执行特定的pytest文件
用例前置和后置方法函数
pytest中的fixture可以任意自定义方法函数,只要加上@pytest.fixture()这个装饰器,那么被修饰的方法就可以被使用
参数化
使用@pytest.mark,parametrize装饰器
断言
assert
生成报告
pytest-HTML、allure插件
失败重跑
pytest支持用例执行失败截图,使用pytest-reunfailures插件(支持失败自动重新执行)
持续集成
和Jenkin持续集成
二、Pytest的安装和规则约束
- 安装: pip install pytest
- 查看安装版本:pytest --version
- pytest文档:https://docs.pytest.org/en/latest/contents.html
- pytest的运行规则:
1、pytest将再当前目录及其子目录中运行所有格式为test_.py或者_test.py的文件
2、类必须以Test开头
三、跑一个demo pytest脚本
import pytest
def func(x):
return x+1
def test_01():
print("---test_01---")
assert func(3) == 5 #assert fail
def test_02():
print("---test_02---")
assert func(3) == 4
if __name__ == '__main__':
pytest.main(["-s","test_sample.py"])
四、用例前置和后置执行
pytest的前置和后置
函数级别:setup/teardown
可以放在类之内也可以写在类之外,对于函数级别的setup/teardown来说效果都是一样的,在执行某个测试用例(一个函数)时都会执行一遍setup/teardown。
1、运行测试方法的始末;
2、运行一次测试用例会运行一此setup和teardown
import pytest
#类之外的函数方法
def test_a():
print("test a")
def test_b():
print("test b")
def setup():
print("setup")
def teardown():
print("teardown")
if __name__ == '__main__':
pytest.main("-s","test_method.py")
import pytest
#类之内
class TestMethod():
def test_a(self):
print("test a")
def test_b(self):
print("test b")
def setup(self):
print("setup")
def teardown(self):
print("teardown")
if __name__ == '__main__':
pytest.main("-s","test_method.py")
放在类之内和类之外的运行效果是一样的:
test_method.py setup
.test a
teardown
setup
.test b
teardown
类级别:setup_class/teardown_class
1、运行于测试类的始末
2、一个测试内只运行一次setup_class和teardown_class
setup_class和teardown_class必须放在类里面,不然的话是不生效的。
import pytest
class TestMethod():
def test_a(self):
print("test a")
def test_b(self):
print("test b")
def setup(self):
print("---setup----")
def teardown(self):
print("---teardown---")
def setup_class(self):
print("***setup class***")
def teardown_class(self):
print("***teardown class***")
if __name__ == '__main__':
pytest.main(["-s","test_class.py"])
运行结果:
test_class.py ***setup class***
---setup----
.test a
---teardown---
---setup----
.test b
---teardown---
***teardown class***
五、pytest的自定义运行规则
pytest里面定义一个文件pytest.ini来自定义用例的运行:
#有该标识则认为为pytest.ini文件
[pytest]
addopt = -s
#表示当前目录下的script文件夹,-可自定义
testpaths = testcase
#表示当前目录下的scripts文件夹下,以test_开头,以.py结尾的所有文件,可自定义
python_files = test_*.py
#表示当前目录下的scripts文件夹下,以test_开头,以.py结尾的所有文件中,以Test_开头的类,可自定义
pytest_classes = Test_*.py
#当前目录下的scripts文件夹下,以test_开头,以.py结尾,以Test_开头的类,以test_开头的方法,可自定义
pytest_functions = test_*
默认规则:
[pytest]
addopts = -v
testpaths = testcase
pytest_files = test_*.py
pytest_class = Test_*
pytest_functions = test_*
pytest -v运行:
testcase/test_class.py::TestMethod::test_a PASSED [ 50%]
testcase/test_class.py::TestMethod::test_b PASSED [100%]
六、pytest断言的使用
判断为真,assert xx
判断xx不为真,assert not xx
判断b包含a,assert a in b
判断a=b,assert a == b
判断a不等于b,assert a != b
import pytest
def test_true():
assert True
def test_false():
assert not False
def test_a_in_b():
res = "{'username':'python','user_id':1}"
assert 'python' in res
def test_code():
code = 200
assert 200 == code
pytest -v运行结果:
testcase/test_assert.py::test_true PASSED [ 16%]
testcase/test_assert.py::test_false PASSED [ 33%]
testcase/test_assert.py::test_a_in_b PASSED [ 50%]
testcase/test_assert.py::test_code PASSED [ 66%]
testcase/test_class.py::TestMethod::test_a PASSED [ 83%]
testcase/test_class.py::TestMethod::test_b PASSED [100%]
七、标记mark的使用
@pytest.mark.test01
pytest提供了标记机制,允许使用mark对测试函数做标记
1.一个测试函数可以多个标记
2.一个mark也可以标记多个测试函数
3.运行参数pytest -m test01
4.运行多个参数pytest -m “test01 or test02”
@pytest.mark.test01
@pytest.mark.test02
def test_b(self):
pass
import pytest
class TestMethod:
@pytest.mark.apitest
@pytest.mark.httptest
def test_a(self):
print("---test a---")
@pytest.mark.apitest
def test_b(self):
print("---test b---")
@pytest.mark.testc
def test_c(self):
print("---test c---")
运行方法:
pytest -m "apitest" #只运行mark标记为apitest的方法
pytest -m "apitest or httptest" #运行mark标记为apitest或者为httptest的方法
pytest -m "not apitest" #运行mark标记为非apitest的其他所有方法
@pytest.mark.skip(reason=none)
1.直接加在方法函数的上面(跳过该函数)
2.直接加在类上面(跳过该类)
import pytest
class TestMethod():
@pytest.mark.skip(reson = "not needed")
def test_a(self):
print("test a")
def test_b(self):
print("test b")
if __name__ == '__main__':
pytest.main(["-s","Test_skip.py"])
运行结果:
Test_skip.py::TestMethod::test_b PASSED [100%]test b
============================== 1 passed in 0.02s ==============================
import pytest
@pytest.mark.skip(reason="not needed")
class TestMethod():
def test_a(self):
print("test a")
def test_b(self):
print("test b")
if __name__ == '__main__':
pytest.main(["-s","Test_skip.py"])
Test_skip.py::TestMethod::test_a SKIPPED [ 50%]
Skipped: not needed
Test_skip.py::TestMethod::test_b SKIPPED [100%]
Skipped: not needed
============================= 2 skipped in 0.02s ==============================
@pytest.mark.skipif(2>1,reason=“我是正确的”)
八、pytest数据参数化
1.传入单个参数,pytest.mark.parametrize(argnames,argvalues)
argnames:参数名
argvalues:参数对应值,类型必须为可迭代类型,一般为list
import pytest
class TestMethod:
@pytest.mark.parametrize("name",["xiaoA","xiaoB"])
def test_a(self,name):
print("--------test a")
print(name)
def test_b(self):
print("--------test b")
if __name__ == '__main__':
pytest.main(["-s","test_params.py"])
运行结果:
test_params.py::TestMethod::test_a[xiaoA] PASSED [ 33%]--------test a
xiaoA
test_params.py::TestMethod::test_a[xiaoB] PASSED [ 66%]--------test a
xiaoB
test_params.py::TestMethod::test_b PASSED [100%]--------test b
============================== 3 passed in 0.03s ==============================
2.传入多个参数
@pytest.mark.parametrize((“username”,“password”),[(“xiaoA”,“123456”),(“xiaoB”,“45678”)])
list每一个元素都是一个元组,元组里的每一个元素和按参数顺序一一对应
import pytest
class TestMethod:
@pytest.mark.parametrize("name",["xiaoA","xiaoB"])
def test_a(self,name):
print("--------test a")
print(name)
@pytest.mark.parametrize(("name","passwd"),[("xiaoA","123456"),("xiaoB","456789")])
def test_b(self,name,passwd):
print("--------test b")
print(name,passwd)
if __name__ == '__main__':
pytest.main(["-s","test_params.py"])
运行结果:
test_params.py::TestMethod::test_a[xiaoA] PASSED [ 25%]--------test a
xiaoA
test_params.py::TestMethod::test_a[xiaoB] PASSED [ 50%]--------test a
xiaoB
test_params.py::TestMethod::test_b[xiaoA-123456] PASSED [ 75%]--------test b
xiaoA 123456
test_params.py::TestMethod::test_b[xiaoB-456789] PASSED [100%]--------test b
xiaoB 456789
九、pytest常用插件(测试报告、失败重跑)
测试报告插件pytest-html
应用场景:自动化测试脚本最终执行是通过还是不通过,需要通过测试报告进行体现
安装:pip3 install pytest-html
使用方式:在pytest里面加–html参数指定生成报告
[pytest]
addopts = -s --html=report.html
testpaths = testcase
pytest_files = Test_*.py
pytest_class = Test_*
pytest_functions = test_*
失败重跑插件
应用场景:当失败后再次尝试
安装:pip3 install pytest-rerunfailures
使用方式:在pytest.ini文件中的ml行参数中增加 --reruns n
如果期望加上出错重试的等待时间,–reruns-delay