Pytest测试框架

​ Pytest是Python一款三方测试框架,用于编写和运行单元测试、集成测试和功能测试

主要特点:

  • 简单易用:Pytest测试框架的API简单易用,可以快速编写测试用例。
  • 灵活多样:Pytest测试框架支持多种测试方式,包括函数式测试、类式测试、参数化测试、fixture测试等。
  • 插件机制:Pytest测试框架支持插件机制,可以通过插件扩展测试框架的功能。
  • 断言机制:Pytest测试框架支持多种断言方式,包括assert语句、assert关键字、assert表达式等。
  • 报告机制:Pytest测试框架支持生成多种测试报告,包括控制台报告、HTML报告、JUnit报告等

安装

1
pip install pytest

编写及运行测试用例

!!! 测试执行直接执行pytest就可以自动查询test开头的测试脚本,创建测试用例需符合框架标准

  1. 新建一个test_开头(必须)的.py文件,如test_user_login.py
  2. 编写一个Test开头(必须)的类,做为测试类
  3. 在类中编写一个test_开头(必须)的方法,作为用例
  • 新建test开头的测试脚本,如test_calc.py,编写测试函数 或 测试类
1
2
3
4
5
6
7
8
9
10
11
12
def test_add(): # 测试函数需以test_开头
''' 测试加法 '''
s = 1 + 2
assert s == 3, f'断言失败, {s} != 3' # 断言

# or

class TestAdd: # 测试类
def test_add_01(self): # 测试方法
''' 测试加法01 '''
s = 1 + 2
assert s == 3, f'断言失败, {s} != 3' # 断言
  • 运行测试用例 一
1
pytest test_calc.py
  • 运行测试用例 二
1
2
3
if __name__ == '__main__':
import pytest
pytest.main(['-x','mytestdir']) # pytest测试当前文件

测试准备及清理 - 夹具

​ Pytest中可以使用不同范围的setup/teardown方法进行测试准备及清理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
def setup_module():
print('测试模块准备')

def teardown_module():
print('测试模块清理')

def setup_function():
print('测试函数准备')

def teardown_function():
print('测试函数清理')

def test_add(): # 测试函数
"""测试加法"""
s = 1 + 2
assert s == 3, f'断言失败, {s} != 3' # 断言


# or
class TestAdd: # 测试类
@classmethod
def setup_class(cls):
print('测试类准备')
@classmethod
def teardown_class(cls):
print('测试类清理')

def setup_method(self):
print('测试方法准备')

def teardown_method(self):
print('测试方法清理')

def test_add_01(self): # 测试方法
"""测试加法01"""
s = 1 + 2
assert s == 3, f'断言失败, {s} != 3' # 断言

参数化(数据驱动)

​ Pytest中可以@pytest.mark.paramitrize()装饰器给测试用例附带参数

1
2
3
4
@pytest.mark.parametrize('a', [1,2,3]) # 参数变量名,数据列表
def test_data(a): # 需要添加和上面同名的参数
"""测试参数化数据"""
print('a =', a)

​ 支持多个参数变量,也支持为每个数据添加自定义说明(id)

1
2
3
4
5
6
7
8
9
data = [(1,2,3), (0,0,0), (-1,2,1)]
ids = ['test1+2', 'test0+0', 'test-1+2']

@pytest.mark.parametrize('a,b,excepted', data, ids=ids) # 多个参数变量名写到同一字符串里
def test_add(a,b,excepted): # 需要添加和上面同名的参数
"""测试加法"""
s = a + b
assert s == excepted, f'断言失败, {s} != {excepted}' # 断言

跳过及期望失败

​ Pytest中可以@pytest.mark.skip()@pytest.mark.skipif()装饰器来跳过或根据条件跳过用例

  • 无条件跳过
1
2
3
@pytest.mark.skip('待实现')
def test_sub():
pass
  • 根据条件跳过
1
2
3
4
5
from platform import platform

@pytest.mark.skipif(platform == 'Windows', reason='不支持Windows')
def test_linux_cmd():
pass
  • 也可以使用@pytest.mark.xfail()来期望用例失败
1
2
3
@pytest.mark.xfail(reason='期望失败,1+1!=3')
def test_add():
assert 1 + 1 == 3

Pytest测试框架的命令行参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 用例挑选相关命令行参数
pytest test_mod.py # 执行模块下的所有用例
pytest testing/ # 执行文件下所有用例
pytest -k 8 # 给指定的测试用例传递测试参数,并测试
# 例如,如果一个测试用例使用了参数化,我想要执行某一个值的测试,而不需要所有都测,就可以使用-k 传递那一个值
pytest test_mod.py::test_func # 执行某个类下的所有用例
pytest test_mod.py::TestClass::test_method # 执行某个单独用例
pytest -m slow # 执行通过标记(Mark)的用例,即所有带@pytest.mark.slow装饰器的用例

pytest --version # 显示pytest导入位置
pytest --fixtures # 显示可用的内置方法参数
pytest -h --help # 显示命令行及配置文件选项帮助信息
pytest -v # -v / --verbose:提高显示详细级别
pytest -s # 捕获用例输出(用例print信息直接输出到命令行)
pytest --no-header # 不显示测试环境
pytest -x # 第1次失败后停止
pytest --maxfail=2 # 2次失败后停止
pytest --pdb # 当用例失败时,会进入调试模式。
# 输入 p 可以查看某个变量的值
# 输入 where 可以查看调用栈信息
# 输入 q 可以退出PDB交互模式
pytest -x --pdb # 在第一次用例失败时进入PDB
pytest --pdb --maxfail=3 # 在前3次失败是进入PDB

# 缓存(重复运行)相关命令行参数
--lf / --last-failed # 运行上次失败的用例
--ff / --failed-first # 先运行上次失败的用例,再运行其他用例
--nf / --last-failed # 先运行上次没有的用例,在运行其他用例
--cache-show # 查看缓存内容
--cache-clear # 清理之前测试的缓存结果

Pytest内置makers标记

在命令行使用pytest –markers可以查看所有可使用(包含用户已注册)标记。
@pytest.mark.filterwarnings(warning):过滤指定警告

1
2
3
4
5
6
7
8
9
10
11
12
@pytest.mark.filterwarnings("ignore:.*")  
def test_example():
import warnings
warnings.warn("This is a warning")
'''
ignore: 忽略所有警告。
error: 将警告转换为错误。
always: 始终显示指定类型的警告。
module: 仅在模块级别显示警告。
once: 每个警告类型仅显示一次。
always:pattern:type: 对特定类型和消息模式的警告进行操作。pattern 是正则表达式,type 是警告类型。
'''

@pytest.mark.skip(reason=None):无条件跳过用例

1
2
3
@pytest.mark.skip(reason="Skipping this test")  
def test_example():
assert True

@pytest.mark.skipif(condition, …, *, reason=…):根据条件跳过用例

1
2
3
@pytest.mark.skipif(condition=True, reason="Condition met")  
def test_example():
assert True

@pytest.mark.xfail(condition, …, *, reason=…, run=True, raises=None, strict=xfail_strict):根据条件期望用例失败

1
2
3
@pytest.mark.xfail  
def test_example():
assert False

@pytest.mark.parametrize(argnames, argvalues):参数化数据驱动 - 跳转
@pytest.mark.usefixtures(fixturename1, fixturename2, …):标记引用(依赖)某些Fixture函数

1
2
3
@pytest.mark.usefixtures("fixture_name")  
def test_example():
assert True

也可以自定义markers

Pytest测试框架的配置项

pytest.ini文件[pytest]中常用配置项如下

配置项 说明
addopts 默认额外的参数;
cache_dir 缓存目录,用于缓存上次执行失败的用例;
markers 注册的自定义标记及说明;
norecursedirs 不遍历的目录;
testpaths 测试目录;
python_files 测试脚本匹配规则,默认test_开头的py文件视为用例,如果有的测试脚本不是以test_开头,可以配置为pythonfiles = *;
python_class 测试类匹配规则,默认Test开头的类视为测试类;
python_functions 测试函数及测试方法匹配规则,默认test_开头的函数及方法视为测试用例;
console_output_style 命令行输出样式,支持classic、progress、counts三种样式;
filterwarnings 过滤的警告;
xfail_strict 启用时,标记为xfail的用例通过时状态为Fail,否则为XPassed。

日志相关配置

配置项 说明
log_print 用例失败时是否显示相关日志;
log_cli 配置为ture时开启命令行日志;
log_file 配置日志文件路径,每次覆盖,不支持追加模式;v
log_cli_level/log_file_level 配置输出到命令行及文件的日志等级;
log_cli_format/log_file_format 配置输出到命令行及文件的日志格式;
log_cli_date_format/log_file_date_format 配置日志的日期格式。

pytest常用断言

assert ‘a’ not in ‘abc’

  • 等于:==
  • 不等于:!=
  • 大于:>
  • 小于:<
  • 属于:in
  • 不属于:not in
  • 大于等于:>=
  • 小于等于:<=
  • 是:is
  • 不是:is not

fixture

pytestfixture 是一种强大的功能,用于为测试提供准备和清理操作。

基本概念

  1. 定义 Fixture

    使用 @pytest.fixture 装饰器来定义一个 fixture 函数。该函数可以返回测试所需的数据或状态。

    1
    2
    3
    4
    5
    import pytest  

    @pytest.fixture
    def sample_data():
    return {"key": "value"}
  2. 使用 Fixture

    在测试函数中,通过将 fixture 名称作为参数传递来使用 fixture。pytest 会自动注入 fixture 的返回值。

    1
    2
    def test_sample_data(sample_data):  
    assert sample_data["key"] == "value"

Fixture 的高级用法

  1. 自动清理

    可以通过在 fixture 函数中使用 yield 关键字来添加清理操作。在 yield 之前的代码是设置阶段,yield 之后的代码是清理阶段。

    1
    2
    3
    4
    5
    6
    7
    @pytest.fixture  
    def sample_file():
    # Setup
    file = open("temp_file.txt", "w")
    yield file
    # Teardown
    file.close()
  2. 作用域

    Fixture 的作用域决定了 fixture 的生命周期。可以通过 scope 参数设置作用域:

    • function(默认):每个测试函数调用一次。
    • class:每个测试类调用一次。
    • module:每个测试模块调用一次。
    • session:测试会话期间调用一次。
    1
    2
    3
    4
    5
    @pytest.fixture(scope="module")  
    def db_connection():
    conn = create_db_connection()
    yield conn
    conn.close()
  3. Fixture 依赖

    Fixtures 可以依赖于其他 fixtures。只需将其他 fixtures 名称作为参数传递即可

    1
    2
    3
    4
    @pytest.fixture  
    def db_data(db_connection):
    # 相当于闭包写法
    return fetch_data_from_db(db_connection)
  4. Fixture 作为参数

    使用 fixture 作为测试函数的参数,以将其注入到测试中

    1
    2
    def test_db_data(db_data):  
    assert db_data is not None

Pytest测试框架的Fixture机制

@pytest.fixture(scope=,params=,autouse=,ids=,name=)
参数说明:
scope :表示的是被@pytest.fixture标记的方法的作用域。 【function:默认,class(类),module(模块),package/session】
params:参数化(支持:列表,元祖,字典列表[{},{},{}],字典元祖[(),(),()])
autouse:True:自动执行,默认False
ids:当 使用params参数化时,给每一个值设置一个变量名,意义不大
name:给表示的是被pytest.fixture标记的方法取一个别名

​ 在Pytest测试框架中,Fixture机制是一种用于管理测试用例依赖关系的机制。Fixture机制可以在测试用例执行之前和之后执行一些操作,例如创建测试数据、打开数据库连接、清理测试数据等。

​ 在Pytest中使用@pytest.fixture装饰器装饰一个函数,该函数可以yield或return返回数据、对象或者一个函数。在测试用例中,使用@pytest.mark.usefixtures装饰器来或将Fixture函数名作为参数或使用Fixture。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import pytest

@pytest.fixture
def user():
return 'admin', '123456'

@pytest.fixture
def login(user): # Fixture函数可以引用(依赖)其他Fixture函数
# 使用的user参数实际为调用user()函数后的返回值
username, password = user
print(f'{username} 登录')
token = 'abcdef'
yield token # yield上为测试准备,yield下为测试清理
print('退出登录’)

def test_add_project(login): # 引用(依赖)login方法
# 使用login参数实际是调用login()函数的返回值即'abcdef'
print('token =', login)

# 也可以标记使用指定Fixture函数,执行其测试准备即清理方法
@pytest.mark.userfixtures('login')
def test_add_project02():
pass # 这种情况下拿不到Fixture函数的返回结果
Fixture生效范围

Fixture函数可以通过scope指定作用范围,Pytest中的Fixture函数支持以下5种范围:

  • Session会话级:scope=‘session’,运行一次Pytest算一次会话。运行期间只setup/teardown一次
    Package包级:scope=‘pacakge’,对每个包Python包setup/teardown一次
  • Module模块级:scope=‘module’,对每个Python脚本setup/teardown一次
  • Class级:scope=‘class’,对每个测试类setup/teardown一次
  • Function级:scope=‘function’,默认,每个测试函数或测试方法setup/teardown一次
1
2
3
4
5
6
7
8
from selenium import webdriver

# 整个运行过程中仅启动关闭一次浏览器
@pytest.fixture(scope='session')
def driver():
dr = webdriver.Chrome()
yield dr
dr.quit()
Fixture数据共享及同名覆盖

​ Fixture函数一般作为公用的辅助方法或全局变量来使用,因此需要在不同用例中都能使用。Pytest框架中使用固定名称的conftest.py文件,来集中管理Fixture函数 。
​ conftest.py文件同级即下级目录中的所有用例可以无需导入,直接使用conftest.py中的所有Fixture函数。
​ conftest.py文件在不同的目录中可以有多个(生效范围不同),同时用例文件中也可以编写Fixture函数,当Fixture函数有重名时,采用“就近”原则,离当前用例最近的Fixture函数生效。

1
2
3
4
5
6
7
8
9
10
11
@pytest.fixture(scope='session')
def base_url():
return 'http://localhost:3000'

def test_base_url(base_url):
print('base_url =', base_url)

if __name__ == '__main__':
# pip install pytest-base-url
import pytest
pytest.main([__file__, '-sq','--base-url=http://www.xxx.com'])
1
2
3
4
5
6
7
8
conftest.py文件的作用:
用来编写Fixture函数
编写钩子Hooks函数
导入其所在目录或包的路径(可以确保项目根目录被导入)

conftest.py所在导入规则:
如果conftest.py所在目录没有__init__.py文件,则Pytest自动导入conftest.py所在目录。
如果有则向上找到最上层的包(最上层一个包含__init__.py的目录),并导入包的所在目录,以确保conftest.py可以使用
Fixture返回函数对象

​ 由于Fixture函数不接收普通参数,无法根据用例需要进行特定的测试准备,此时我们一般需要封装一个实用函数并导入使用,也可以把函数定义到Fixture函数的内部,并返回该函数对象,这样用例无需导入便可直接使用功能函数。

  • 常规导入模块方式
1
2
3
4
5
# auth.py
def login(username, password):
# ...
token = 'abcdef'
return token
1
2
3
4
5
6
# test_project_api.py
from .auth import login

def test_add_project():
token = login('admin', 'abc123')
# ...
  • 封装成Fixture方式
1
2
3
4
5
6
7
8
# conftest.py
@pytest.fixture
def login():
def _login(username, password):
# ...
token = ‘abcdef’
return token
return _login
1
2
3
4
5
6
# test_project_api.py
def test_add_project(login): # 无需导入直接使用
# 使用的login参数实际是login()函数的调用结果,
# 即内部的_login函数
token = login('admin', 'abc123')
# ...
直接autouse=True传递给fixture(autouse=True)

将会在有效范围的test用例全部默认执行

conftest.py 用法

fixture函数的发现顺序从测试类开始,然后是测试模块,然后是conftest.py文件,最后是内置和第三方插件。你还可以使用conftest.py文件来实现本地每个目录的插件

conftest.pypytest 框架中的一个特殊文件,用于定义和共享 fixtures、hooks 和插件等。这些内容可以在同一目录及其子目录的测试文件中被自动发现和使用。无需导入

同名方法可能会被覆盖掉

  1. 定义共享 Fixtures

    conftest.py 中定义的 fixtures 可以在该文件所在目录及其所有子目录的测试文件中使用。这使得 fixture 的定义可以在多个测试模块间共享。

    1
    2
    3
    4
    5
    6
    # conftest.py  
    import pytest

    @pytest.fixture
    def sample_data():
    return {"key": "value"}
  2. 作用域和清理

    可以在 conftest.py 中定义具有不同作用域和清理操作的 fixtures,以便在项目的多个测试中重用这些 fixtures。

    1
    2
    3
    4
    5
    6
    7
    8
    # conftest.py  
    import pytest
    # 可以将autouse设置为True,此后在该作用域内,所有测试用例都会默认执行
    @pytest.fixture(scope="module",autouse=True)
    def db_connection():
    conn = create_db_connection()
    yield conn
    conn.close()
  3. 不同传递参数和使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    # conftest.py 
    import pytest
    @pytest.fixture(scope="session")
    def db_connection():
    print("db_setup")
    yield 222
    print("db_teardown")


    @pytest.fixture(autouse=True)
    def temp_file(request):
    print("file_setup")
    yield 111
    print("file_teardown")


    # test_01.py
    class TestConf:
    def test_01_db(self, db_connection, request):
    print("db_testing")
    print(db_connection) # 222
    print(request.getfixturevalue('temp_file')) # 111
    # 如果使用了autouse=True,可以利用定义的函数参数来接受
    def test_02_file(self, request):
    print("db_testing")
    print(request.getfixturevalue('temp_file')) # 111

使用marks标记测试用例

通过使用@pytest.mark你可以轻松地在测试用例上设置元数据。标记只对测试用例有效,对fixtures方法无效。

  • skip - 始终跳过该测试用例
  • skipif - 遇到特定情况跳过该测试用例
  • xfail - 遇到特定情况,产生一个“期望失败”输出
  • parametrize - 在同一个测试用例上运行多次调用(译者注: 参数化数据驱动)

标记测试用例 - @pytest.mark

使用 @pytest.mark 可以为测试用例添加标记

1
2
3
4
5
import pytest  

@pytest.mark.smoke
def test_login():
pass

使用@pytest.mark.smoke标记后,可以使用pytest -m smoke 运行所有被@pytest.mark.smoke标记的用例
为了防止用户意外输错标记名称,还可以将smoke标记添加在pytest.ini markers参数中。但执行参数还要加上--strict才能生效。这样用户输入-m参数,不在markers中就会报错

1
2
3
4
[pytest]
addopts = --strict
markers =
smoke

标记跳过 - @pytest.mark.skip

使用 @pytest.mark.skip 标记可以跳过某个测试用例

1
2
3
4
5
import pytest  

@pytest.mark.skip(reason="This test is not implemented yet")
def test_checkout():
pass

标记条件跳过 - 使用 @pytest.mark.skipif 标记可以根据条件跳过测试用例

1
2
3
4
5
6
7
8
9
10
import pytest  

@pytest.fixture(scope="session")
def skip_condition():
# 逻辑判断
return True
# 通过前置处理条件,看是否需要进行测试该测试项
@pytest.mark.skipif(condition=skip_condition(), reason="Skipping this test based on condition from fixture")
def test_example():
assert 1 == 1

标记期望失败 - 使用 @pytest.mark.xfail 标记可以标记测试用例为期望失败

1
2
3
4
5
import pytest  
# 期望这个用例失败,当这个用例结果为failed时,实际结果为PASSED
@pytest.mark.xfail(reason="This test is expected to fail")
def test_checkout():
assert 1 == 2

自定义标记 - @pytest.mark

Pytest测试框架的参数化机制

​ Pytest提供了三种参数化方式:

使用@pytest.mark.paramitrize()标记进行数据驱动测试 parametrize

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 单循环
@pytest.mark.parametrize("name", ["老白"])
def test_login(name):
print('登录', user)


# 多循环
# 单元素
users = ["老白1", "老白2", "老白3"]
@pytest.mark.parametrize("name", users)
def test_login(name):
print('登录', user)

# 多元素 - 二维数组或元组
users = [
('admin', '123456'),
('kevin','abc123'),
('lily', 'abcdef')
]

@pytest.mark.parametrize('user,pwd', users)
def test_login(user, pwd):
print('登录', user, pwd)

使用Fixture函数的params参数,进行参数化测试准备

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 users = [
('admin', '123456'),
('kevin','abc123'),
('lily', 'abcdef')
]

@pytest.fixture(params=users)
def login(request): # request是系统内置Fixture
user, pwd = request.param
print('登录', user, pwd)

def test_add_project(login): """测试不同用户添加项目"""
# ...

使用钩子函数pytest_generate_tests(),动态成多条用例

1
2
3
4
5
6
7
8
# conftest.py
def pytest_addoption(parser): # 添加命令行参数
parser.addoption("--filepath", action="append",default=[], help="run file list")

def pytest_generate_tests(metafunc):
if "filepath" in metafunc.fixturenames:
filepaths = metafunc.config.getoption("filepath")
metafunc.parametrize("filepath",filepaths)
1
2
3
# test_a.py
def test_a(filepath):
print('test_a', filepath)

Pytest测试框架的收集机制

​ 在Pytest测试框架中,收集机制是一种用于自动收集测试用例的机制。Pytest测试框架会自动搜索指定目录下的测试文件,并收集其中的测试用例。

以下是Pytest测试框架的收集机制的一些常用用法:

默认收集规则

Pytest测试框架的默认收集规则是搜索以test_开头或以_test结尾的文件,并收集其中的测试用例。例如,test_sum.py、sum_test.py、test_sum.py::test_sum等文件和测试用例都会被收集。

根据正则匹配收集用例

可以使用pytest命令的-k选项来指定收集指定目录下的测试文件。例如,可以使用以下命令来收集tests目录下的所有测试文件:

1
2
pytest -k tests
# Pytest测试框架会自动搜索tests目录下的测试文件,并收集其中的测试用例

自定义收集规则

可以使用pytest_collection_modifyitems钩子函数来自定义收集规则

1
2
3
4
5
6
7
def pytest_collection_modifyitems(items):
for item in items:
if "slow" in item.keywords:
item.add_marker(pytest.mark.slow)
'''
使用pytest_collection_modifyitems钩子函数来修改测试用例列表。如果测试用例的关键字中包含slow,则为该测试用例添加@pytest.mark.slow标记。
'''

unittest.TestCase 支持

Pytest支持unittest开箱即用的基于Python的测试

要使用运行现有unittest样式的测试套件pytest,请键入pytest tests。pytest将自动收集unittest.TestCase子类及其test方法test_*.py*_test.py文件

Pytest测试框架扩展 - 插件

如果安装了插件,则pytest自动查找并集成它,无需激活它

pytest用例标记插件 - @pytestrail.case (需安装pytest-testrail 插件)

1
2
3
4
5
6
import pytest  
import pytestrail

@pytestrail.case('C1234') # TestRail 用例 ID
def test_example():
assert True

pytest测试顺序插件 - @pytest.mark.order() (需安装 pytest-order 插件)

1
2
3
4
5
6
7
@pytest.mark.order(1)  
def test_first():
assert True

@pytest.mark.order(2)
def test_second():
assert True

pytest 设置最大运行时间 - @pytest.mark.timeout (需安装 pytest-timeout 插件)

1
2
3
4
@pytest.mark.timeout(2)  # Timeout after 2 seconds  
def test_example():
import time
time.sleep(3)

pytest-rerunfailures - 失败重新运行

pytest-cov:覆盖率报告,与分布式测试兼容

pytest-xdist:将测试分发到CPU和远程主机,以盒装模式运行,允许分段故障,在looponfailing模式下运行,自动重新运行文件更改的失败测试。

pytest-instafail:在测试运行期间报告失败。

pytest-bdd使用行为驱动的测试编写测试。

Pytest自定义插件开发

插件包含一个或多个钩子(hooks)方法函数。[编写钩子(hooks)方法解释了如何自己编写钩子(hooks)方法函数的基础知识和细节。pytest通过调用以下插件的[指定挂钩来实现配置,收集,运行和报告的所有方面:

  • 内置插件:从pytest的内部_pytest目录加载。
  • 外部插件:已安装的第三方插件,通过其打包元数据中的入口点发现。
  • conftest.py插件:在测试目录中自动发现的模块

pytest.ini 配置项

配置项 说明 默认值
addopts 额外(默认)命令行参数
cache_dir 设置存储缓存插件内容的目录 .pytest_cache
confcutdir 设置向上搜索conftest.py文件的目录
console_output_style 设置用例执行进度的控制台输出样式,支持classic、progress和count三种 progress
log_cli 设置True表示在终端输出日志信息
log_cli_level 设置日志级别
log_cli_format 设置time.strftime()与日志记录捕获格式化日期时将使用的兼容字符串
markers 定义自定义标记,用于标记测试用例

日志记录

pytest 已经集成了logging日志,可以在不需要封装logging的情况下使用,只需导入 logging即可使用

pytest结合allure

安装 allure

1
$ pip install allure-pytest

在pytest 执行语句中 加入–alluredir,即可生成allure报告

1
$ pytest --alluredir=test-results  

allure在pytest框架内的标记使用

  1. epic描述@allure.epic(epic_name)
    • 标记测试用例所属的史诗。
1
2
3
@allure.epic("User Management")  
def test_user_management():
# 测试代码
  1. 模块名称@allure.feature(feature_name)
    标记测试用例所属的特性或功能模块。
1
2
3
@allure.feature("Login")  
def test_login():
# 测试代码
  1. 用例标题@allure.title(title)
    • 定义用例标题
1
2
3
@allure.title("Login")  
def test_login():
# 测试代码
  1. 用例名称@allure.story(story_name)
    标记测试用例所属的用户故事或子功能。
1
2
3
@allure.story("User authentication")  
def test_user_authentication():
# 测试代码

image-20240827222318811

  1. 用例等级@allure.severity(severity_level)
    标记测试用例的严重性级别。blocker、critical、normal、minor、trival
1
2
3
@allure.severity('critical')  
def test_critical_scenario():
# 测试代码
  1. 用例描述@allure.description(description)
    添加测试用例的描述信息。
1
2
3
@allure.description("This test case verifies the login functionality.")  
def test_login():
# 测试代码
  1. 操作步骤@allure.step(step_description)
    标记测试中的步骤,可以在报告中展现测试执行过程。
1
2
3
@allure.step("Enter username")  
def step_enter_username():
# 步骤代码
  1. 添加附件allure.attachment(data, name, attachment_type)
  • 作用:添加附件到测试报告,可以是文本、图片、文件等。

  • 参数:data 表示要附加的数据,name 为附件名称,attachment_type 表示附件类型。

  • 示例:

    1
    2
    3
    4
    5
    6
    7
    8
    def test_login(username, password):  
    allure.attachment("Test Description", "This test case verifies the login functionality.")

    # 执行登录操作
    login_result = perform_login(username, password)

    # 断言登录结果
    assert login_result == "success"

image-20240827222728367

  1. 缺陷链接@allure.issue(url)
    • 添加 Bug 或任务的链接。
1
2
3
@allure.issue("https://github.com/username/project/issue123")  
def test_login_issue():
# 测试代码
  1. 测试用例链接@allure.testcase(url)
    • 添加关联的测试用例链接。
1
2
3
@allure.testcase("https://jira.example.com/browse/TEST-123")  
def test_login_testcase():
# 测试代码
  1. 定义链接@allure.link(url, name)
    • 添加通用的链接,可以是任何网页链接。
1
2
3
@allure.link("https://www.example.com", name="Example Website")  
def test_example_link():
# 测试代码
  1. @allure.owner(owner)
    • 标记测试用例的所有者或责任人。
1
2
3
@allure.owner("John Doe")  
def test_owner():
# 测试代码