pytest 框架

pytest是Python的单元测试框架,同unittest框架类似,但pytest框架使用起来更简洁,效率更高。

特点:

  • Python 编写测试用例,易于上手。
  • 支持单元测试和功能测试。
  • 灵活的初始化清除机制,同时支持参数化。
  • 灵活挑选测试用例执行,或对某些预期失败的case标记成失败。
  • 支持重复执行失败的case。
  • 支持运行由unittest编写的测试用例。
  • 第三方插件多,并且可自定义扩展。

前言

pytest : 单元测试框架

pytest-html : 生成测试报告

allure-pytest : 更加完善的测试报告

pytest-ordering : 手动控制执行顺序

pytest-xdist : 支持并发执行

pytest-sugar :支持了用例执行进度条

pytest-cov : 支持显示覆盖率


简单使用

编写的测试用例代码文件跟unittest差不多,类名要以Test开头, 用例以 test_ 开头,或者以 _test 结尾

1
2
3
4
5
6
7
8
9
10
11
12
13
class Test_Error_Password:

def test_C001001(self):
print('\n用例C001001')
assert 1 == 1

def test_C001002(self):
print('\n用例C001002')
assert 2 == 2

def test_C001003(self):
print('\n用例C001003')
assert 3 == 2

pytest 中用例的检查点 直接用 Python 的 assert 断言。

在shell中使用命令执行,使用pytest -s 其中-s为显示代码中print到屏幕输出流的内容

更详细的执行信息,包括每个测试类、测试函数的名字,可以加上参数 -v,这个参数可以和 -s 合并为 -sv,pytest -sv

setup和teardown

对于自动化测试来说,用例执行前的初始化工作,和执行后的收尾工作很重要,在pytest中分为几种情况:

  • 模块级别,也就是在整个测试脚本文件中的用例集开始前后,对应的是:

    • setup_module
    • teardown_module
  • 类级别,在类中的所有用例集执行前后,对应的是:

    • setup_class
    • teardown_class
  • 在类中呢,也可以在进一步划分,在每一个方法执行前后,对应:

    • setup_method
    • teardown_method
  • 函数级别,在用例函数之前后,对应:

    • setup_function
    • teardown_function
  • 目录级别,就是针对整个目录执行

    需要初始化的目录下面创建 一个名为 conftest.py 的文件,里面内容如下所示

    1
    2
    3
    4
    5
    6
    7
    import pytest 

    @pytest.fixture(scope='package',autouse=True)
    def st_emptyEnv():
    print(f'\n#### 初始化-目录')
    yield
    print(f'\n#### 清除-目录')

    挑选测试用例执行

  • 挑选模块执行

    1
    pytest cases\login\test_login.py
  • 指定目录

    1
    2
    pytest cases  # 指定一个
    pytest cases1 case2 # 指定多个
  • 指定模块中的类或方法

    1
    2
    pytest cases\login\test_login.py::Test_Password  # 指定类
    pytest cases\login\test_login.py::Test_Password::test_C001001 # 指定方法
  • 根据名字指定测试项

    使用 -k 参数

    1
    pytest -k C001001 -s

    注:-k 后面的名字

    • 可以是测试方法的名字,可以是类的名字,可以是模块文件名,可以是目录的名字

    • 是大小写敏感的,支持模糊匹配

    • 可使用逻辑运算

      1
      2
      3
      pytest -k "not C001001" -s # 名字中不包含
      pytest -k "错 and 密码" -s # 名字中同时包含
      pytest -k "错 or 密码" -s # 名字中包含任意一个
  • 根据标签

    给某个方法加上标签 tag0

    1
    2
    3
    4
    5
    6
    7
    8
    import pytest

    class Test_Password:

    @pytest.mark.tag0
    def test_C001021(self):
    print('\n用例C001021')
    assert 1 == 1

    给整个类加上标签

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @pytest.mark.tag0
    class Test_Password:
    def test_C001021(self):
    print('\n用例C001021')
    assert 1 == 1

    # 可以同时加多个标签
    @pytest.mark.tag1
    @pytest.mark.tag2
    class Test_Password2:
    def test_C001021(self):
    print('\n用例C001021')
    assert 1 == 2

    同时可以定义一个全局变量 pytestmark 为 整个模块文件 设定标签

    1
    2
    3
    4
    import pytest
    pytestmark = pytest.mark.tag1 # 定义一个标签

    pytestmark = [pytest.mark.tag1, pytest.mark.tag2] # 定义多个标签

    运行命令行指定标签

    1
    pytest cases -m xxtag -s

    配置文件

以上执行都为单独的命令,执行用例;我们可以使用配置文件来进行配置来指定需要执行的用例

可以建立一个pytest.ini文件,实现相关的配置:

1
2
3
4
5
6
[pytest]
addopts = -s -v
testpaths = ./cases
python_files = test_*.py
python_classes = Test*
python_functions = test_*

pytest.ini文件必须位于项目的根目录,而且也必须叫做pytest.ini

参数分析:

  • addopts可以搭配相关的参数,比如-s。多个参数以空格分割

    • -s,在运行测试脚本时,为了调试或打印一些内容,我们会在代码中加一些print内容,但是在运行pytest时,这些内容不会显示出来。如果带上-s,就可以显示了。
    • -v,使输出结果更加详细。
  • testpaths配置测试用例的目录,

    • 因为我们用例可能分布在不同的目录或文件中,那么这个scripts就是我们所有文件或者目录的顶层目录。其内的子文件或者子目录都要以test_开头,pytest才能识别到。
    • 想要在这个总目录下执行其中某个具体的脚本文件
    1
    2
    3
    [pytest]
    testpaths = ./scripts/
    python_files = test_case_01.py

    这么写就是执行scripts目录下面的test_case_01.py这个文件。

  • python_classes则是说明脚本内的所有用例类名必须是以Test开头,当然,你也可以自定义为以Test_开头,而类中的用例方法则当然是以test_开头。

  • python_functions则是说脚本内的所有用例函数以test_开头才能识别。

跳过用例

使用@pytest.mark.skipif(condition, reason)将它装饰在需要被跳过用例的的函数上面:

  • condition表示跳过用例的条件。
  • reason表示跳过用例的原因。
1
2
3
4
5
6
7
8
9
import pytest

@pytest.mark.skip(condition='跳过这个用例')
def test_case_01():
assert 1

@pytest.mark.skipif(condition=1 < 2, reason='如果条件为true就跳过用例')
def test_case_02():
assert 1

注:要想打印出reason信息,则可以在配置文件中的addopts参数的-s变为-rs

标记预期失败

有时我们事先知道测试函数会执行失败,但又不想直接跳过,而是希望显示的提示。

使用 pytest.mark.xfail实现预见错误功能:

1
xfail(condiition, reason, [raises=None, run=True, strict=False])

必传参数:

  • condition,预期失败的条件,当条件为真的时候,预期失败。
  • reason,失败的原因。

预期失败的几种情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import pytest

class TestCase(object):
@pytest.mark.xfail(1 < 2, reason='预期失败, 执行失败')
def test_case_01(self):
""" 预期失败, 执行也是失败的 """
print('预期失败, 执行失败')
assert 0

@pytest.mark.xfail(1 < 2, reason='预期失败, 执行成功')
def test_case_02(self):
""" 预期失败, 但实际执行结果却成功了 """
print('预期失败, 执行成功')
assert 1

pytest 使用 x 表示预见的失败(XFAIL)。如果预见的是失败,但实际运行测试却成功通过,pytest 使用 X 进行标记(XPASS)

1
2
[pytest]
xfail_strict=true # 可以控制预期失败结果成功的情况输出结果显示

参数化

实现用例传参,支持DDT数据驱动测试,参数化测试,即每组参数都独立执行一次测试

pytest.mark.parametrize(argnames, argvalues)

  • argnames表示参数名。
  • argvalues表示列表形式的参数值。
1
2
3
4
5
6
7
8
import pytest

mobile_list = ['10010', '10086']

@pytest.mark.parametrize('mobile', mobile_list)
def test_register(mobile):
""" 通过手机号注册 """
print('注册手机号是: {}'.format(mobile))

固件

固件(Fixture)是一些函数,pytest 会在执行测试函数之前(或之后)加载运行它们,也称测试夹具。

我们可以利用固件做任何事情,其中最常见的可能就是数据库的初始连接和最后关闭操作。

在初始化操作目录级别的时候就使用了固件

1
2
3
4
5
6
7
import pytest 

@pytest.fixture(scope='package',autouse=True)
def st_emptyEnv():
print(f'\n#### 初始化-目录')
yield
print(f'\n#### 清除-目录')

作用域-scope

pytest通过scope参数来控制固件的使用范围,也就是作用域。

在定义固件时,通过 scope 参数声明作用域,可选项有:

  • function: 函数级,每个测试函数都会执行一次固件;
  • class: 类级别,每个测试类执行一次,所有方法都可以使用;
  • module: 模块级,每个模块执行一次,模块内函数和方法都可使用;
  • session: 会话级,一次测试只执行一次,所有被找到的函数和方法都可用。

默认的作用域为 function

初始化与后清除

Pytest 使用 yield 关键词将固件分为两部分,yield 之前的代码属于预处理,会在测试前执行;yield 之后的代码属于后处理,将在测试完成后执行。

常用插件及其用法

pytest-html

在配置文件中,添加参数:

1
2
[pytest]
addopts = -s --html=report/report.html

allure-pytest

Allure框架是一个灵活的轻量级多语言测试报告工具,它不仅以web的方式展示了简洁的测试结果,而且允许参与开发过程的每个人从日常执行的测试中最大限度的提取有用信息。
从开发人员(dev,developer)和质量保证人员(QA,Quality Assurance)的角度来看,Allure报告简化了常见缺陷的统计:失败的测试可以分为bug和被中断的测试,还可以配置日志、步骤、fixture、附件、计时、执行历史以及与TMS和BUG管理系统集成,所以,通过以上配置,所有负责的开发人员和测试人员可以尽可能的掌握测试信息。

:allure工具之前,它依赖Java环境,我们还需要先配置Java环境

地址:https://github.com/allure-framework/allure2

使用步骤:

  • 配置pytest.ini文件。
  • 编写用例并执行。
  • 使用allure工具生成html报告。

来看配置pytest.ini

1
2
3
4
5
6
7
[pytest]
addopts = -v -s --html=report/report.html --alluredir ./report/result
testpaths = ./cases/
python_files = test_allure_case.py
python_classes = Test*
python_functions = test_*
# xfail_strict=true

用例执行完,在项目的根目下,会自动生成一个report目录,目录下result和assets目录是allure插件生成的测试报告文件,但此时该目录内还没有什么HTML报告,只有一些相关数据。

1
allure generate report/result -o report/allure_html --clean

使用以上命令生成测试报告

report目录下新建一个allure_html目录,而这个目录内有index.html才是最终的allure版本的HTML报告;如果你是重复执行的话,使用--clean清除之前的报告

1
allure open .\allure_report # 打开allure测试报告

在使用allure生成报告的时候,在编写用例阶段,还可以有一些参数可以使用:

  • title,自定义用例标题,标题默认是用例名。

  • description,测试用例的详细说明。

  • feature和story被称为行为驱动标记,因为使用这个两个标记,通过报告可以更加清楚的掌握每个测试用例的功能和每个测试用例的测试场景。或者你可以理解为feature是模块,而story是该模块下的子模块。

  • allure中对bug的严重(severity)级别也有定义,allure使用

    severity来标识测试用例或者测试类的bug级别,分为blocker,critical,normal,minor,trivial5个级别。一般,bug分为如下几个级别:

    • Blocker级别:中断缺陷(客户端程序无响应,无法执行下一步操作),系统无法执行、崩溃或严重资源不足、应用模块无法启动或异常退出、无法测试、造成系统不稳定。
    • Critical级别:即影响系统功能或操作,主要功能存在严重缺陷,但不会影响到系统稳定性。比如说一个服务直接不可用了,微信不能发消息,支付宝不能付款这种,打开直接报错。
    • Major:即界面、性能缺陷、兼容性。如操作界面错误(包括数据窗口内列名定义、含义是否一致)、长时间操作无进度提示等。
    • Normal级别:普通缺陷(数值计算错误),是指非核心业务流程产生的问题,比如说知乎无法变更头像,昵称等。这个要看自己的定义。
    • Minor/Trivial级别:轻微缺陷(必输项无提示,或者提示不规范),比如各种影响体验,但不影响使用的内容。
  • dynamic,动态设置相关参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import pytest
import allure

@allure.title('测试用例标题1')
@allure.description('这是测试用例用例1的描述信息')
@allure.feature('登录模块')
class TestCaseLogin(object):
@allure.story('登录模块下的子模块: test1')
def test_case_01():
assert 1
@allure.severity(allure.severity_level.CRITICAL)
def test_case_02():
assert 0

@pytest.mark.parametrize('name', ['动态名称1', '动态名称2'])
def test_case_03(self, name):
allure.dynamic.title(name)

pytest-ordering

手动控制执行顺序

1
@pytest.mark.run(order=x)   # x 是一个整数

x: 0 > 正数 > 没有参与的用例 > 负数

pytest-xdist

并发的执行测试用例

在配置文件中添加:

1
2
3
4
5
6
7
[pytest]
addopts = -v -s --html=report/report.html -n=auto
;addopts = -s --alluredir ./report/result
testpaths = ./scripts/
python_files = test_case_01.py
python_classes = Test*
python_functions = test_*

就是这个-n=auto

  • -n=auto,自动侦测系统里的CPU数目。
  • -n=numprocesses,也就是自己指定运行测试用例的进程数。

pytest-sugar

支持了用例执行进度条,不需要配置,只需 下载插件即可

改变了 pytest 的默认外观,添加了一个进度条,并立即显示失败的测试

pytest-cov

支持显示覆盖率

配置--cov=./scripts


总结:pytest是一个很好也是常用的自动化测试框架,无论是在web自动化,还是接口自动化中都能够发挥很好的测试价值。

都看到这里了,不赏点银子吗^v^