一篇教程精通pytest
Posted 上海一亩地
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一篇教程精通pytest相关的知识,希望对你有一定的参考价值。
目录
黑盒与白盒
在测试行业中,所有的测试工作分成黑盒测试和白盒测试两类。其中黑盒测试是不涉及软件内部代码逻辑的测试。测试对象是软件功能、性能、兼容性、安全健壮性、可移植性、可靠性等等,衡量黑盒测试工作量的指标是检测的功能点数量,衡量黑盒测试工作效果的指标是bug发现数量(虽然不应该这样,有的软件bug就是少,但是没办法,老板们都这么干)。而白盒测试是针对代码的测试,测试对象是类、方法和静态函数。衡量白盒测试的指标是覆盖率,包括语句覆盖率、条件覆盖率、分支覆盖率等等,白盒测试只能用自动化的方式实现,手工?你试试要请多少人干活。
一个新软件完成,没有经过测试。一般一轮测试是人工手动的功能测试,将所有功能点测试一遍,然后统计重要的,易出错的功能点(bug常常扎堆出现)。将这些易出错的功能点的测试用例再拿来进行二轮甚至三轮测试。而对于重要的功能点,把这些功能点相关的用例全部用自动化去实现,这样每出现一个新版本就跑一次自动化看看有没有大问题,俗称冒烟测试,。最终将稳定的不经常改动的功能点进行自动化回归测试,回归测试中大量采用自动化,很少用手工测试。
综上:
- 一轮全手工,功能全覆盖,需要大量人力。
- 易出错功能的进行二轮。
- 重要的功能,用例自动化执行,以后版本迭代时进行冒烟测试。
- 稳定的功能,用例自动化执行,定期进行回归测试。
什么是自动化测试
自动化是一种测试方法,但不是测试类型。自动化就是代替人工。所以既有黑盒自动化测试也有白盒自动化测试。
- 黑盒自动化:自动化擅长的领域就那么三种,命令行测试(subprocess+paramiko)、API接口测试(requests+UIautomation)、web页面测试(selenium)。所以如果涉及桌面测试,就很难了,非常不稳定。
- 白盒自动化测试: 主要就是使用静态代码扫描工具(Sonarqube)+ 白盒框架(pytest)去测试一个个函数和类,以及统计覆盖路。
pytest特点
- pytest是单元测试框架,能批量执行其他函数,统计执行结果,生成测试报告。unittest也可以,但没有pytest强大。
- pytest能实现跳过用例或者rerun用例。
- pytest有非常美观的测试报告。
- pytest有丰富的插件支持。
pytest命名规则
- 待测模块名(py文件名)必须用test_开头,或者_test结尾。
- 待测类名必须以Test开头并且不能有init方法。
- 待测方法必须以test开头,不必有下划线
pytest的运行方式
- main主函数运行
- 命令行运行
- pytest.ini配置文件运行
- nodeid的方式,即模块::类名::函数名
main函数方式:
新建login_test.py
# 主函数方式
import pytest
class TestLogin():
def test_01_haha(self):
print("hahahaha")
if __name__ =="__main__":
pytest.main(["-vs"])
命令行方式
直接执行pytest -vs
-v表示详细模式,每个函数显示模块和所在类。
-s是显示打印信息。
-n支持多核多线程并发运行测试。多个待测函数可以同时跑。
–reruns=NUM 失败重跑数
-x 有用例失败就停止测试,后面测试不继续跑
–maxfail=NUM 最多出现n次失败后停止测试
-k 只执行函数名中包含特定字符串的用例
-m 只执行标记的用例
–html xxxx/xxxx.html 给出文件路径,生成测试报告
>pytest -vs
======================================================================================= test session starts ========================================================================================
platform win32 -- Python 3.8.5, pytest-6.2.4, py-1.10.0, pluggy-0.13.1 -- c:\\python38\\python.exe
cachedir: .pytest_cache
metadata: {'Python': '3.8.5', 'Platform': 'Windows-10-10.0.19041-SP0', 'Packages': {'pytest': '6.2.4', 'py': '1.10.0', 'pluggy': '0.13.1'}, 'Plugins': {'allure-pytest': '2.9.43', 'forked': '1.3.0',
'html': '3.1.1', 'metadata': '1.11.0', 'ordering': '0.6', 'rerunfailures': '10.1', 'xdist': '2.3.0'}}
rootdir: E:\\python-code\\pytest_learn
plugins: allure-pytest-2.9.43, forked-1.3.0, html-3.1.1, metadata-1.11.0, ordering-0.6, rerunfailures-10.1, xdist-2.3.0
collected 1 item
case/login_test.py::TestLogin::test_01_haha hahahaha
PASSED
======================================================================================== 1 passed in 0.02s =========================================================================================
可以发现main方法中的内容其实就是命令行中的内容。
-n并行运行
import pytest
import time
class TestLogin():
def test_01_haha(self):
time.sleep(3)
print("hahahaha")
def test_02_hoho(self):
time.sleep(3)
print("hohohoho")
if __name__ =="__main__":
#pytest.main(["-vs"])
pytest.main(["-vs","-n 2"])
>pytest -vs -n 2
===================================== test session starts ======================================
platform win32 -- Python 3.8.5, pytest-6.2.4, py-1.10.0, pluggy-0.13.1 -- c:\\python38\\python.exe
cachedir: .pytest_cache
metadata: {'Python': '3.8.5', 'Platform': 'Windows-10-10.0.19041-SP0', 'Packages': {'pytest': '6.
2.4', 'py': '1.10.0', 'pluggy': '0.13.1'}, 'Plugins': {'allure-pytest': '2.9.43', 'forked': '1.3.
0', 'html': '3.1.1', 'metadata': '1.11.0', 'ordering': '0.6', 'rerunfailures': '10.1', 'xdist': '
2.3.0'}}
rootdir: E:\\python-code\\pytest_learn
plugins: allure-pytest-2.9.43, forked-1.3.0, html-3.1.1, metadata-1.11.0, ordering-0.6, rerunfail
ures-10.1, xdist-2.3.0
[gw0] win32 Python 3.8.5 cwd: E:\\python-code\\pytest_learn
[gw1] win32 Python 3.8.5 cwd: E:\\python-code\\pytest_learn
[gw0] Python 3.8.5 (tags/v3.8.5:580fbb0, Jul 20 2020, 15:57:54) [MSC v.1924 64 bit (AMD64)]
[gw1] Python 3.8.5 (tags/v3.8.5:580fbb0, Jul 20 2020, 15:57:54) [MSC v.1924 64 bit (AMD64)]
gw0 [2] / gw1 [2]
scheduling tests via LoadScheduling
case/login_test.py::TestLogin::test_02_hoho
case/login_test.py::TestLogin::test_01_haha
[gw0] PASSED case/login_test.py::TestLogin::test_01_haha
[gw1] PASSED case/login_test.py::TestLogin::test_02_hoho
====================================== 2 passed in 3.63s =======================================
node id的方式:
pytest指定某个模块下某个类下的某个方法。中间用双冒号:: 隔开
import pytest
class TestLogin():
def test_01_haha(self):
print("hahahaha")
if __name__ =="__main__":
# pytest.main(["-vs"])
# pytest.main(["-vs","./login_test.py"]) # 这里可以指定文件或文件夹,main方法可以放在系统的任何地方。
pytest.main(["-vs", "login_test.py::TestLogin::test_01_haha"])
pytest.ini配置文件运行方式
通过预先编写配置文件再执行的方式才是最常用的执行方式,企业自动化的测试调度也需要代码化。
pytest.ini是pytest的基础配置文件
- 位置:放在项目根目录
- 编码:必须使用ANSI编码,可以用notepad++更改文件编码。
- 作用:改变pytest的默认行为。
案例:
[pytest]
# 加配置
addopts = -vs
# 测试用例文件夹
testpaths = ./case
# 配置测试搜索的文件名模式
python_files = test_*.py
# 测试搜索类名
python_classes = Test*
# 测试搜索函数名
python_functions = test
# 只执行带标签的用例,如果跑全部,就不加这个
markers = smoke
# 执行多个标签可以加and 和 or。 markers = "smoke or product"
如何给用例添加标记呢?
使用@pytest.mark.标记的
import pytest
import time
class TestLogin():
@pytest.mark.run(order=3)
@pytest.mark.smoke
def test_01_haha(self):
time.sleep(3)
print("hahahaha")
失败用例重跑
加–reruns可以在用例执行失败后再多跑n次。
import pytest
import time
class TestLogin():
def test_01_haha(self):
time.sleep(3)
print("hahahaha")
def test_02_hoho(self):
time.sleep(3)
print("hohohoho")
def test_03_fail(self):
time.sleep(3)
assert 1==2
if __name__ =="__main__":
#pytest.main(["-vs"])
pytest.main(["-vs","-n 3","--reruns=2"])
C:\\Python38\\python.exe E:/python-code/pytest_learn/case/login_test.py
============================= test session starts =============================
platform win32 -- Python 3.8.5, pytest-6.2.4, py-1.10.0, pluggy-0.13.1 -- C:\\Python38\\python.exe
cachedir: .pytest_cache
metadata: {'Python': '3.8.5', 'Platform': 'Windows-10-10.0.19041-SP0', 'Packages': {'pytest': '6.2.4', 'py': '1.10.0', 'pluggy': '0.13.1'}, 'Plugins': {'allure-pytest': '2.9.43', 'forked': '1.3.0', 'html': '3.1.1', 'metadata': '1.11.0', 'ordering': '0.6', 'rerunfailures': '10.1', 'xdist': '2.3.0'}}
rootdir: E:\\python-code\\pytest_learn\\case
plugins: allure-pytest-2.9.43, forked-1.3.0, html-3.1.1, metadata-1.11.0, ordering-0.6, rerunfailures-10.1, xdist-2.3.0
gw0 I / gw1 I / gw2 I
[gw0] win32 Python 3.8.5 cwd: E:\\python-code\\pytest_learn\\case
[gw1] win32 Python 3.8.5 cwd: E:\\python-code\\pytest_learn\\case
[gw2] win32 Python 3.8.5 cwd: E:\\python-code\\pytest_learn\\case
[gw0] Python 3.8.5 (tags/v3.8.5:580fbb0, Jul 20 2020, 15:57:54) [MSC v.1924 64 bit (AMD64)]
[gw1] Python 3.8.5 (tags/v3.8.5:580fbb0, Jul 20 2020, 15:57:54) [MSC v.1924 64 bit (AMD64)]
[gw2] Python 3.8.5 (tags/v3.8.5:580fbb0, Jul 20 2020, 15:57:54) [MSC v.1924 64 bit (AMD64)]
gw0 [3] / gw1 [3] / gw2 [3]
scheduling tests via LoadScheduling
login_test.py::TestLogin::test_03_fail
login_test.py::TestLogin::test_01_haha
login_test.py::TestLogin::test_02_hoho
[gw1] PASSED login_test.py::TestLogin::test_02_hoho
[gw0] PASSED login_test.py::TestLogin::test_01_haha
[gw2] RERUN login_test.py::TestLogin::test_03_fail
login_test.py::TestLogin::test_03_fail
[gw2] RERUN login_test.py::TestLogin::test_03_fail
login_test.py::TestLogin::test_03_fail
[gw2] FAILED login_test.py::TestLogin::test_03_fail
================================== FAILURES ===================================
___________________________ TestLogin.test_03_fail ____________________________
[gw2] win32 -- Python 3.8.5 C:\\Python38\\python.exe
self = <case.login_test.TestLogin object at 0x000002B1BCE46070>
def test_03_fail(self):
time.sleep(3)
> assert 1==2
E assert 1 == 2
E +1
E -2
login_test.py:15: AssertionError
=========================== short test summary info ===========================
FAILED login_test.py::TestLogin::test_03_fail - assert 1 == 2
==================== 1 failed, 2 passed, 2 rerun in 9.89s =====================
Process finished with exit code 0
–maxfail和-k参数自己试试
pytest调整用例执行顺序
在待测用例的上方加装饰器@pytest.mark.run(order=n)能调整case执行顺序。
import pytest
import time
class TestLogin():
@pytest.mark.run(order=3)
def test_01_haha(self):
time.sleep(3)
print("hahahaha")
@pytest.mark.run(order=1)
def test_02_hoho(self):
time.sleep(3)
print("hohohoho")
@pytest.mark.run(order=2)
def test_03_fail(self):
time.sleep(3)
assert 1==2
if __name__ =="__main__":
pytest.main(["-vs"])
跳过执行测试用例
无条件跳过
用@pytest.mark.skip(reason=“无条件跳过”),reason这里加一段话解释为什么跳过,也可以不加。
@pytest.mark.run(order=1)
@pytest.mark.skip(reason="无条件跳过")
def test_02_hoho(self):
time.sleep(3)
print("hohohoho")
有条件跳过用
@pytest.mark.skipif(age>18,reason="成年")
def test_02_hoho(self):
time.sleep(3)
print("hohohoho")
生成测试报告
[pytest]
# 加配置
addopts = -vs --html ./report/report.html
# 测试用例文件夹
testpaths = ./case
# 配置测试搜索的文件名模式
python_files = test_*.py
# 测试搜索类名
python_classes = Test*
# 测试搜索函数名
python_functions = test
# 只执行带标签的用例,如果跑全部,就不加这个
markers = smoke
# 执行多个标签可以加and 和 or。 markers = "smoke or product"
或者 命令执行pytest --html ./report/report.html
以上是关于一篇教程精通pytest的主要内容,如果未能解决你的问题,请参考以下文章
单元测试界的高富帅,Pytest框架,手把手教学,从入门到精通
单元测试界的高富帅,Pytest框架,手把手教学,从入门到精通
超详细从入门到精通,pytest自动化测试框架实战教程-allure测试报告