文章目录
一、pytest.ini介绍
是pytest框架的主配置文件,实际生产中主要用来规范日志的格式或日志文件保存的位置,增加用例标签等等,总之简单易用,属于pytest学习的重要知识点。
特别注意:
pytest.ini文件命名不能修改,文件中第一行必须用【pytest】申明这是一个pytest的ini文件
二、自定义mark标签
我门在编写自动化测试用例时,会有各种类型的场景用例,我们又不想一次性运行全部,只想运行其中的几个,这时我们可以借助mark标签来管理测试用例,mark标签是任意取的,但是要避开Python和pytest关键字,运行标签用 - m 来运行,如:pytest -m div testcases/menus/test_menus1.py::TestMenus
特别注意:
多个标签要换行,且不能在顶格写,要有空格
1.案例:只执行div的测试用例
# encoding=utf-8
import pytest
class TestMenus:
@pytest.mark.div
def test_menu1(self):
assert 1 == 1
@pytest.mark.div
@pytest.mark.add
def test_menu2(self):
assert 1 == 1
def test_menu3(self):
assert 2 == 5
if __name__ == '__main__':
pytest.main()
执行命令行:
D:\project_development\api_pytest>pytest -vs -m div testcases/menus/test_menus1.py::TestMenus
执行测试结果
======================================================= test session starts =======================================================
platform win32 -- Python 3.9.6, pytest-5.2.1, py-1.10.0, pluggy-0.13.1
rootdir: D:\project_development\api_pytest, inifile: pytest.ini
plugins: allure-pytest-2.9.43, Faker-14.1.0, testreport-1.1.2
collected 3 items / 1 deselected / 2 selected
testcases/menus/test_menus1.py::TestMenus::test_menu1 ----这是前置方法----
PASSED
testcases/menus/test_menus1.py::TestMenus::test_menu2 PASSED----这是后置方法----
================================================= 2 passed, 1 deselected in 0.10s =================================================
2.注册标记来防止拼写错误
自定义标记可以简化测试工作,让我们用指定的标记运行某个测试子集。但是,标记很容易拼错,比如把@pytest.mark.smoke拼成@pytest.mark.somke,默认情况下。这不会引起程序错误。pytest会以为这是你创建的另一个标记。为了避免拼写错误。可以在pytest.ini文件里注册标记
标记完可以通过:pytest --markers 查看
案例1:注册标记使用正确
[pytest]
;python_files = check*
;python_functions = check*
;python_classes = CHECK*
markers =
smoke: Run the smoke test functions for tasks project
get: Run the test functions that test tasks.get()
div
add
addopts= -v -s --capture=no
test_menus.py
# encoding=utf-8
import pytest
import logging
class TestMenus:
@pytest.mark.smoke
def test_menu1(self):
logging.info('执行测试用例1')
assert 1 == 1
@pytest.mark.smoke
def test_menu2(self):
logging.info('执行测试用例2')
assert 1 == 1
def test_menu3(self):
logging.info('执行测试用例3')
assert 2 == 5
if __name__ == '__main__':
pytest.main()
执行命令:pytest -m smoke
collected 3 items / 1 deselected / 2 selected
testcases/menus/test_menus1.py::TestMenus::test_menu1 ----这是前置方法----
---------------------------------------------------------- live log call ----------------------------------------------------------
2022-10-30 10:41:22 [INFO] 执行测试用例1 (test_menus1.py:9)
PASSED
testcases/menus/test_menus1.py::TestMenus::test_menu2
---------------------------------------------------------- live log call ----------------------------------------------------------
2022-10-30 10:41:22 [INFO] 执行测试用例2 (test_menus1.py:15)
PASSED----这是后置方法----
================================================= 2 passed, 1 deselected in 0.13s =================================================
案例2:注册标记使用错误
执行命令:pytest -m smoke1(smoke1没有注册)
把smoke故意写错了,也没有报错,只是给我们一个警告,只需要我们在ini里注册下就不会有警告了
pytest -m smoke1
D:\project_development\api_pytest>pytest -m smoke1
======================================================= test session starts =======================================================
platform win32 -- Python 3.9.6, pytest-5.2.1, py-1.10.0, pluggy-0.13.1 -- d:\python3.9\python.exe
cachedir: .pytest_cache
metadata: {'Python': '3.9.6', 'Platform': 'Windows-10-10.0.19044-SP0', 'Packages': {'pytest': '5.2.1', 'pluggy': '0.13.1'}, 'Plugins
': {'allure-pytest': '2.9.43', 'Faker': '14.1.0', 'html': '3.2.0', 'metadata': '2.0.3', 'testreport': '1.1.2'}, 'JAVA_HOME': 'C:\\Pr
ogram Files\\Java\\jdk1.8.0_291'}
rootdir: D:\project_development\api_pytest, inifile: pytest.ini
plugins: allure-pytest-2.9.43, Faker-14.1.0, html-3.2.0, metadata-2.0.3, testreport-1.1.2
collected 3 items / 3 deselected
====================================================== 3 deselected in 0.06s ======================================================
3.指定pytest忽略某些目录(norecurse)
pytest执行测试搜索时,会递归遍历所有子目录,包括某些你明知道没必要遍历的目录。遇到这种情况,你可以使用norecurse选项简化pytest的搜索工作。
pytest.ini
把menus用例目录注销了,不会去执行这个目录下的用例了
[pytest]
norecursedirs = .* venv menus *.egg dist build
url = https://blog.csdn.net/YZL40514131
markers =
smoke: Run the smoke test functions for tasks project
get: Run the test functions that test tasks.get()
div
add
addopts = -v -s --capture=no
log_cli = true
log_cli_level = info
log_cli_format = %(asctime)s [%(levelname)s] %(message)s (%(filename)s:%(lineno)s)
log_cli_date_format = %Y-%m-%d %H:%M:%S
log_file = ./logs/all.log
log_file_level = info
log_file_format = %(asctime)s [%(levelname)s] %(message)s (%(filename)s:%(lineno)s)
log_file_date_format = %Y-%m-%d %H:%M:%S
执行结果
collected 3 items
testcases/works/test_work1.py::TestWork::test_h ----这是前置方法----
读取到配置文件的url地址:https://blog.csdn.net/YZL40514131
用例:https://blog.csdn.net/YZL40514131
PASSED
testcases/works/test_work1.py::TestWork::test_work1 PASSED
testcases/works/test_work1.py::TestWork::test_work2 PASSED----这是后置方法----
======================================================== 3 passed in 0.14s ========================================================
也可以指定多个目录
norecursedirs = .* venv menus works *.egg dist build
4.指定测试目录(testpaths)
norecuredirs告诉pytest哪些路径不用访问,而testpaths则指示pytest去哪里访问。testpaths是一系列相对于根目录的路径,用于限定测试用例的搜索范围。只有在pytest未指定文件目录参数或测试用例标识符时,该选项才有作用
pytest.ini
如果我们只想执行testcases/menus下的测试用例,则可以把testcases/menus放到testpaths里
[pytest]
testpaths = testcases/menus
url = https://blog.csdn.net/YZL40514131
markers =
smoke: Run the smoke test functions for tasks project
get: Run the test functions that test tasks.get()
div
add
addopts = -v -s --capture=no
log_cli = true
log_cli_level = info
log_cli_format = %(asctime)s [%(levelname)s] %(message)s (%(filename)s:%(lineno)s)
log_cli_date_format = %Y-%m-%d %H:%M:%S
log_file = ./logs/all.log
log_file_level = info
log_file_format = %(asctime)s [%(levelname)s] %(message)s (%(filename)s:%(lineno)s)
log_file_date_format = %Y-%m-%d %H:%M:%S
执行命令
collected 3 items
testcases/menus/test_menus1.py::TestMenus::test_menu1 ----这是前置方法----
---------------------------------------------------------- live log call ----------------------------------------------------------
2022-10-30 11:34:00 [INFO] 执行测试用例1 (test_menus1.py:9)
PASSED
testcases/menus/test_menus1.py::TestMenus::test_menu2
---------------------------------------------------------- live log call ----------------------------------------------------------
2022-10-30 11:34:00 [INFO] 执行测试用例2 (test_menus1.py:15)
PASSED
testcases/menus/test_menus1.py::TestMenus::test_menu3
---------------------------------------------------------- live log call ----------------------------------------------------------
2022-10-30 11:34:00 [INFO] 执行测试用例3 (test_menus1.py:19)
PASSED----这是后置方法----
======================================================== 3 passed in 0.09s ========================================================
5.如果我既指定了testpaths和 norecursedirs ,而且两个是一样的,结果会怎样
从上面两次的运行结果可以看出,如果既指定了testpaths和 norecursedirs ,而且两个是一样的,则是按照testpaths执行的
collected 3 items
testcases/menus/test_menus1.py::TestMenus::test_menu1 ----这是前置方法----
---------------------------------------------------------- live log call ----------------------------------------------------------
2022-10-30 11:38:28 [INFO] 执行测试用例1 (test_menus1.py:9)
PASSED
testcases/menus/test_menus1.py::TestMenus::test_menu2
---------------------------------------------------------- live log call ----------------------------------------------------------
2022-10-30 11:38:28 [INFO] 执行测试用例2 (test_menus1.py:15)
PASSED
testcases/menus/test_menus1.py::TestMenus::test_menu3
---------------------------------------------------------- live log call ----------------------------------------------------------
2022-10-30 11:38:28 [INFO] 执行测试用例3 (test_menus1.py:19)
PASSED----这是后置方法----
======================================================== 3 passed in 0.09s ========================================================
三.自定义运行时的默认参数
pytest有多个经常使用的参数,如 - vs 来打印更详细的信息,但是每次都要手动输入很麻烦,例如上个案例 pytest -vs -m div testcases/menus/test_menus1.py::TestMenus
这是需要用到 addopts 配置默认运行参数
特别注意
addopts 运行时参数(可添加多个命令行参数,空格分隔,所有参数与命令行一致)
案例:
pytest.ini
[pytest]
markers =
smoke: Run the smoke test functions for tasks project
get: Run the test functions that test tasks.get()
div
add
addopts= -v -s
url = https://blog.csdn.net/YZL40514131
test_menus1.py
# encoding=utf-8
import pytest
class TestMenus:
@pytest.mark.div
def test_menu1(self):
assert 1 == 1
@pytest.mark.div
@pytest.mark.add
def test_menu2(self):
assert 1 == 1
def test_menu3(self):
assert 2 == 5
if __name__ == '__main__':
pytest.main()
命令行执行:pytest -m div testcases/menus/test_menus1.py::TestMenus
pytest -m div testcases/menus/test_menus1.py::TestMenus
执行结果
collected 3 items / 1 deselected / 2 selected
testcases/menus/test_menus1.py::TestMenus::test_menu1 ----这是前置方法----
PASSED
testcases/menus/test_menus1.py::TestMenus::test_menu2 PASSED----这是后置方法----
================================================= 2 passed, 1 deselected in 0.10s =================================================
四.格式化日志
案例
pytest.ini
[pytest]
markers =
smoke: Run the smoke test functions for tasks project
get: Run the test functions that test tasks.get()
div
add
addopts= -v -s --capture=no
log_cli = true
log_cli_level = info
log_cli_format = %(asctime)s [%(levelname)s] %(message)s (%(filename)s:%(lineno)s)
log_cli_date_format = %Y-%m-%d %H:%M:%S
log_file = ./logs/all.log
log_file_level = info
log_file_format = %(asctime)s [%(levelname)s] %(message)s (%(filename)s:%(lineno)s)
log_file_date_format = %Y-%m-%d %H:%M:%S
test_menus.py
# encoding=utf-8
import pytest
import logging
class TestMenus:
@pytest.mark.div
def test_menu1(self):
logging.info('执行测试用例1')
assert 1 == 1
@pytest.mark.div
@pytest.mark.add
def test_menu2(self):
logging.info('执行测试用例2')
assert 1 == 1
def test_menu3(self):
logging.info('执行测试用例3')
assert 2 == 5
if __name__ == '__main__':
pytest.main()
执行命令:pytest testcases/menus/test_menus1.py::TestMenus
执行结果
testcases/menus/test_menus1.py::TestMenus::test_menu1 ----这是前置方法----
---------------------------------------------------------- live log call ----------------------------------------------------------
2022-10-29 23:37:46 [INFO] 执行测试用例1 (test_menus1.py:9)
PASSED
testcases/menus/test_menus1.py::TestMenus::test_menu2
---------------------------------------------------------- live log call ----------------------------------------------------------
2022-10-29 23:37:46 [INFO] 执行测试用例2 (test_menus1.py:15)
PASSED
testcases/menus/test_menus1.py::TestMenus::test_menu3
---------------------------------------------------------- live log call ----------------------------------------------------------
2022-10-29 23:37:46 [INFO] 执行测试用例3 (test_menus1.py:19)
FAILED----这是后置方法----
============================================================ FAILURES =============================================================
______________________________________________________ TestMenus.test_menu3 _______________________________________________________
self = <testcases.menus.test_menus1.TestMenus object at 0x00000173C6D7A760>
def test_menu3(self):
logging.info('执行测试用例3')
> assert 2 == 5
E assert 2 == 5
E -2
E +5
testcases\menus\test_menus1.py:20: AssertionError
-------------------------------------------------------- Captured log call --------------------------------------------------------
INFO root:test_menus1.py:19 执行测试用例3
=================================================== 1 failed, 2 passed in 0.17s ===================================================
五.自定义测试文件命名规则
测试用例默认命名规则
除非pytest命令指定到测试用例文件,否则测试用例文件命名应该以 test_开头或者以_test结尾。
测试函数命名,测试类的方法命名应该以test_开头。
测试类命名应当以Test开头。
tips: 测试类的不应该有构造函数。
python_files 自定义测试文件命名规则
python_classes = Test_* 自定义测试类命名规则
python_functions= test_* check_* 自定义测试方法命名规则
pytest 框架可以通过pytest.ini配置文件自定义命名规则,在某些特定场景下可能会用到。
特别注意:
想控制多种文件类型,用空格隔开
案例
pytest.ini文件
[pytest]
python_files = check*
python_functions = check*
python_classes = CHECK*
markers =
smoke: Run the smoke test functions for tasks project
get: Run the test functions that test tasks.get()
div
add
addopts= -v -s --capture=no
log_cli = true
log_cli_level = info
log_cli_format = %(asctime)s [%(levelname)s] %(message)s (%(filename)s:%(lineno)s)
log_cli_date_format = %Y-%m-%d %H:%M:%S
log_file = ./logs/all.log
log_file_level = info
log_file_format = %(asctime)s [%(levelname)s] %(message)s (%(filename)s:%(lineno)s)
log_file_date_format = %Y-%m-%d %H:%M:%S
check_menus.py
# encoding=utf-8
import pytest
import logging
class CHECKclass:
@pytest.mark.div
def check_menu1(self):
logging.info('执行测试用例1')
assert 1 == 1
@pytest.mark.div
@pytest.mark.add
def check_menu2(self):
logging.info('执行测试用例2')
assert 1 == 1
def check_menu3(self):
logging.info('执行测试用例3')
assert 2 == 5
if __name__ == '__main__':
pytest.main()
执行命令行:pytest
collected 3 items
testcases/menus/check_menus.py::CHECKclass::check_menu1 ----这是前置方法----
---------------------------------------------------------- live log call ----------------------------------------------------------
2022-10-30 00:23:49 [INFO] 执行测试用例1 (check_menus.py:9)
PASSED
testcases/menus/check_menus.py::CHECKclass::check_menu2
---------------------------------------------------------- live log call ----------------------------------------------------------
2022-10-30 00:23:49 [INFO] 执行测试用例2 (check_menus.py:15)
PASSED
testcases/menus/check_menus.py::CHECKclass::check_menu3
---------------------------------------------------------- live log call ----------------------------------------------------------
2022-10-30 00:23:49 [INFO] 执行测试用例3 (check_menus.py:19)
FAILED----这是后置方法----