顶部右侧自定义内容
顶部左侧自定义内容
当前位置:首页 > 我的文章 > 正文

pytest框架详解

作者:jm8X08LA发布时间:2024-12-17 11:42分类: 我的文章 浏览:163评论:0


导读:Pytest框架 介绍 1.简介 pytest是纯python编写的自动化测试框架,可以支持python语法编写测试用例,是一个非常成熟的全功能的Python测试框架。 主要有以下...

Pytest框架
介绍
1.简介
pytest是纯python编写的自动化测试框架,可以支持python语法编写测试用例,是一个非常成熟的全功能的Python测试框架。

主要有以下几个特点:

简单灵活,容易上手;
支持参数化;
能够支持简单的单元测试和复杂的功能测试,还可以结合selenium、requests做自动化测试;
pytest具有很多第三方插件,并且可以自定义扩展。
安装:pip install pytest

官方文档:https://docs.pytest.org

2.第一个pytest用例
def test01():
assert 1 == 1
3.pytest命名规范
测试模块:以 test
开头命名,如:test_login.py,或以 _test 结尾;

测试类:必须以Test开头命名,且测试类中不能有 init 方法;

测试方法/测试函数:必须以test开头。

4.常用参数
可以通过pytest -h来查看所有可用参数。pytest的参数有很多,下面是归纳一些常用的参数:

无参数:读取路径下符合条件的所有类、函数、方法全部执行;
-v:打印详细运行日志;
-s:输出调试信息,包括print打印的信息;命令行输入:pytest -s;
-x:运行用例失败立即停止运行

–maxfail
用例失败数达到某个设定的值停止运行
pytest --maxfail=[num]

-m 运行所有@pytest.mark.[标记名] 标记的用例

比如:用例标记 @pytest.mark.high

pytest -m=hign 或者 pytest -m hign。# 表示只执行有此标记hight的case。

pytest -m="hign or smoke" 或者 pytest -m "hign or smoke" # 表示两种标记都执行

–reruns=num:失败用例重跑num次。需要安装 pytest-rerunfailures 插件模块。

-n参数,启用多线程或分布式运行测试用例。需要安装pip install pytest-xdist 插件模块。
命令行输入:pytest -vs -n=2

-k: 指定运行某个或某些用例
pytest -k ‘类名’
pytest -k ‘方法名’
pytest -k ‘类名 and not 方法名’ # 运行类里所有方法,不包含某个方法
命令行输入:pytest -vs -k=01

python程序运行pytest:

caseNameString = " or ".join(caseNameList)
cmd = f"python -m pytest -k \"{caseNameString}\" --alluredir ./allure --clean-alluredir"
os.system(cmd)
-k的值支持中文的哟~
参数-n,启用多线程或分布式运行测试用例。需要安装pip install pytest-xdist 插件模块。
命令行输入:pytest -vs -n=2

5.实现数据驱动
Pytest 测试框架的数据驱动是由 pytest 自带的pytest.mark.parametrize()来实现的。

@pytest.mark.parametrize() 装饰器接收两个参数:
第一个参数以字符串的形式存在,它代表能被测试函数所能接受的参数,如果被测试函数有多个参数,则以逗号分隔;
第二个参数用于保存测试数据。如果只有一组数据,以列表的形式存在,如果有多组数据,以列表嵌套元组的形式存在(例如: [1,1]或者[(1,1), (2,2)])。

实例

class Test01:
@pytest.mark.parametrize('a,b,expect',[(1,1,1),(2,3,5)])
def test_001(self,a,b,expect):
print('测试a+b的结果')
assert a+b==expect

@pytest.mark.parametrize('c,d,expect', [(2, 4, 2), (9, 10, 1)])
def test_002(self,c,d,expect):
    assert d-c==expect

if name == 'main':
pytest.main([file, '-k','test_001'])
6.pytest fixtures
6.1 fixture用途

fixture主要用来做初始化环境以及测试结束后的数据清除。
pytest fixture与setup,teardown功能一样,但比之更加灵活,完全可以代替setup,teardown。
6.2 fixture参数详解

@pytest.fixture(scope='function',params=None,autouse=False,ids=None,name=None)
yield

fixture装饰器,相当于setup,测试用例的前置

  • scope: 有四个级别参数'function(默认)'、'class'、'module'、'session'。
  • params:一个可选的参数列表,列表中每个数据都可以作为用例的输入。也就说有多少数据,就会形成多少用例。可以通过request.param来获取该次调用的参数。
  • autouse:如果True,自动调用fixture功能。如果为False则需要调用fixture。
  • ids:每个字符串id的列表,每个字符串对应于params这样他们就是测试ID的一部分。如果没有提供ID它们将从params自动生成。
  • name:fixture的名称。这默认为装饰函数的名称。如果fixture在定义它的统一模块。
    yield:这个关键字之后的代码相当于teardown,测试用例的后置。

6.3 fixture的作用范围

fixture里面有个scope参数可以控制fixture的作用范围:session>module>class>function
-function:每一个函数或方法都会调用
-class:每一个类调用一次,一个类中可以有多个方法
-module:每一个.py文件调用一次,该文件内又有多个function和class
-session:是多个文件调用一次,可以跨.py文件调用,每个.py文件就是module

6.4 调用fixture的三种方法

方式1:函数或类里直接传fixture的函数名称

@pytest.fixture()
def test_fixture(self):
    print('\n用例开始执行fix1\n')
    yield
    print('\n用例执行结束fix1\n')

def test_a(self, test_fixture):
    print('runing')
    assert 1 == 1

方式2: 使用装饰器@pytest.mark.usefixtures()修饰需要运行的用例(可以叠加使用多个装饰器)

@pytest.fixture()
def test_fixture(self):
    print('\n用例开始执行fix1\n')
    yield
    print('\n用例执行结束fix1\n')

@pytest.fixture()
def test_fixture2(self):
    print('\n用例开始执行fix2\n')
    yield
    print('\n用例执行结束fix2\n')

@pytest.mark.usefixtures('test_fixture')
@pytest.mark.usefixtures('test_fixture2')
def test_b(self):
    assert 1==1

执行结果

7.skip – 跳过测试
7.1 pytest.skip() 用于函数内,跳过测试用例

@pytest.mark.parametrize('a,b,expect',[(1,1,1),(2,3,5)])
def test_001(self,test1,a,b,expect):
pytest.skip('跳过此测试用例')
assert a+b==expect
用于函数外,跳过测试用例

@pytest.mark.skip(reason="功能未实现")
@pytest.mark.parametrize('a,b,expect',[(1,1,1),(2,3,5)])
def test_001(self,test1,a,b,expect):
assert a+b==expect
用在函数外,条件condition为True时,跳过用例

@pytest.mark.skipif(condition=True,reason="功能未实现")
@pytest.mark.parametrize('a,b,expect',[(1,1,1),(2,3,5)])
def test_001(self,test1,a,b,expect):
assert a+b==expect

8.rerunfailure–失败重跑,插件pytest-rerunfailures
安装

前提条件: pytest (>=5.3) 和python >=3.6
安装:pip install pytest-rerunfailures
查看安装版本:pip show pytest-rerunfailures
pytest-rerunfailures 使用

命令行参数: --reruns n(重新运行次数)–reruns-delay m(等待运行秒数)
使用装饰器: @pytest.mark.flaky(reruns=5, reruns_delay=2)
命令行实例

!/usr/bin/env python3

!coding:utf-8

import pytest
import random

def test_simple_assume():

每次case运行的值为1或者2,具有随机性

r = random.randint(1, 3)
assert r == 1

if name == 'main':
pytest.main(['Test_demo01.py', '-v', '--reruns=2', '--reruns-delay 2'])
配置执行次数越多(如--reruns=2),执行的成功率越高。

命令行参数:
--reruns n(重新运行次数),--reruns-delay m(等待运行秒数)

9.Mark装饰器之order执行顺序
需要先安装插件

cmd命令窗口:pip install pytest-ordering
在pycharm中File-->settings-->Project-->Python Interpreter-->点击+号-->搜索pytest-ordering安装。

查看安装版本:pip show pytest-ordering

使用方法:

控制用例执行顺序的方法;
在需要调整用例执行顺序的函数(或方法)前增加,如@pytest.mark.run(order=x),x表示数字;
执行顺序,由小到大、由正到负、未标记的在正数后、负数前执行,顺序为:1,2,3,无标记,-3,-2,-1;
实例

import pytest

class Test_Class3():
@pytest.mark.run(order=2)
def test_case1(self):
print("测试方法1")

@pytest.mark.run(order=1)
def test_case2(self):
    print("测试方法2")

@pytest.mark.run(order=3)
def test_case3(self):
    print("测试方法3")

if name == 'main':
pytest.main(['Test_demo02.py' '-s'])

10.setup、teardown
setup_class()和 teardown_class()函数
需要定义在测试类中,定义在类外不起作用。
setup_class()定义场景,比如:创建日志对象,创建数据库的连接,创建接口的请求对象等。
teardown_class()定义场景,比如:销毁日志对象,销毁数据库的连接,销毁接口的请求对象。
"""
函数需要定义在测试类中,定义在类外不起作用。
setup_method()和 teardown_method(),在每个测试方法之前/之后执行。定义场景,比如:打开浏览器/关闭浏览器。
setup_class()定义场景,比如:创建日志对象,创建数据库的连接,创建接口的请求对象等。
teardown_class()定义场景,比如:销毁日志对象,销毁数据库的连接,销毁接口的请求对象。
"""

import pytest

class Test_setUp_tearDown:

# 方法级,前置函数
def setup_method(self):
    # print("setup_method(self):在每个测试方法之前执行")
    print("在每个测试方法之前执行")

# 方法级,后置函数
def teardown_method(self):
    # print("teardown_method(self):在每个测试方法之后执行\n")
    print("在每个测试方法之后执行")

# 类级,前置函数
def setup_class(self):
    # print("setup_class(self):每个测试类之前执行一次\n")
    print("每个测试类之前执行一次")

# 类级,后置函数
def teardown_class(self):
    # print("teardown_class(self):每个测试类之后执行一次")
    print("每个测试类之后执行一次")

# 测试用例a
def test_a(self):
    print("test_a方法")
    assert True

# 测试用例b
def test_b(self):
    print("test_b方法")
    assert True

if name == 'main':
pytest.main()
pytest钩子函数

在pytest中,钩子函数是一种特殊的函数,用于在测试执行过程中的特定阶段插入自定义逻辑。pytest提供了许多内置的钩子函数,这些钩子函数允许您在测试的不同阶段进行自定义操作。以下是pytest中常用的钩子函数及其作用

1.作用在类以外的钩子函数
1 setup()/tear_down()
def setup(): print("这是一个setup")
def teardown(): print("这是一个teardown")
2 setup_module()/teardown_module()

    setup_module():在测试模块开始之前运行,用于设置模块级别的资源或配置。可以在该钩子函数中执行一次性的模块设置。

    teardown_module():在测试模块结束之后运行,用于清理模块级别的资源或配置。可以在该钩子函数中执行一次性的模块清理。

    def setup_module(self):
        print("这个是模块级别的setup_module")

    def teardown_module(self):
        print("这个是模块级别的teardown_module")

3 setup_function/teardown_function

每条用例执行前执行一次,不会作用于class中的test_case

    setup_function():在每个测试函数开始之前运行,用于设置单个测试函数的资源或配置。可以在该钩子函数中执行每个测试函数的准备工作。

    teardown_function():在每个测试函数结束之后运行,用于清理单个测试函数的资源或配置。可以在该钩子函数中执行每个测试函数的清理工作。      

    def setup_function()

        print("setup_function模块中每条用例执行前执行一次!")
    def teardown_function():
        print("teardown_function 模块中每条用例执行后执行一次!") 

2.作用在类中的钩子函数
setup_class():在每个测试类开始之前运行,用于设置单个测试类的资源或配置。可以在该钩子函数中执行每个测试类的准备工作。

    teardown_class():在每个测试类结束之后运行,用于清理单个测试类的资源或配置。可以在该钩子函数中执行每个测试类的清理工作。

    setup_method():在每个测试方法开始之前运行,用于设置单个测试方法的资源或配置。可以在该钩子函数中执行每个测试方法的准备工作。

    teardown_method():在每个测试方法结束之后运行,用于清理单个测试方法的资源或配置。可以在该钩子函数中执行每个测试方法的清理工作。

3.pytest_runtest_makereport 钩子函数
pytest_runtest_makereport 是一个重要的钩子,它在测试运行期间被调用,用于生成测试报告。通过覆盖这个钩子,你可以自定义测试报告的行为,例如在测试失败时执行某些操作,或者收集额外的信息。

示例

import pytest

@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):

execute all other hooks to get the report object

outcome = yield
rep = outcome.get_result()

# we only look at actual failing test calls, not setup/teardown
if rep.when == "call" and rep.failed:
    # 自定义逻辑:当测试失败时的操作
    print(f"Test {item.name} failed")
    # 你可以在这里添加更多的逻辑,例如记录日志、发送邮件等

在这个示例中,我们使用 @pytest.hookimpl 装饰器定义了一个 pytest_runtest_makereport 钩子函数。参数 tryfirst=True 表示这个钩子应该尽可能早地被调用,而 hookwrapper=True 表示这是一个钩子包装器,它会先执行其他注册的钩子函数,然后执行自己的逻辑。

钩子详解

item:当前正在运行的测试项。
call:当前测试阶段(setup、call 或 teardown)。
rep:测试报告对象,包含测试的结果信息。
示例中的逻辑

outcome = yield:这部分代码使用 yield 来执行其他注册的钩子函数,并获取它们的结果。这是 hookwrapper 的典型用法。
rep = outcome.get_result():获取测试报告对象。
条件判断:if rep.when == "call" and rep.failed: 检查当前阶段是否为测试执行阶段(而非 setup 或 teardown),并且测试是否失败。
自定义逻辑:在测试失败时执行自定义的逻辑,例如打印一条消息。
完整的插件示例

如果你需要将这个钩子放入一个插件中,可以创建一个 .py 文件(例如 my_plugin.py),并在其中定义这个钩子:

import pytest

class MyPlugin:
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(self, item, call):

execute all other hooks to get the report object

outcome = yield
rep = outcome.get_result()

    # we only look at actual failing test calls, not setup/teardown
    if rep.when == "call" and rep.failed:
        # 自定义逻辑:当测试失败时的操作
        print(f"Test {item.name} failed")
        # 你可以在这里添加更多的逻辑,例如记录日志、发送邮件等

def pytest_configure(config):
config.pluginmanager.register(MyPlugin(), "my_plugin")

def pytest_unconfigure(config):
plugin = getattr(config, "my_plugin", None)
if plugin is not None:
config.pluginmanager.unregister(plugin)
在这个示例中,我们定义了一个 MyPlugin 类,并在其中实现了 pytest_runtest_makereport 钩子。我们还在 pytest_configure 和 pytest_unconfigure 函数中注册和注销插件。

通过这种方式,你可以轻松地扩展 pytest 的功能,自定义测试报告的行为。

4 pytest_sessionfinish(session, exitstatus) 钩子函数
pytest_sessionfinish 是 pytest 提供的一个钩子(hook),它允许你在整个测试会话结束时执行一些操作。这个钩子在所有测试项运行完毕后被调用,但在此之前所有测试收集、设置(setup)、执行以及清理(teardown)都已经完成。这意味着你可以利用这个钩子来做一些总结性的工作,比如输出一些统计信息,关闭资源,清理环境等。

这个钩子有两个参数:

session: 一个 Session 对象,提供了对当前测试会话的信息访问。
exitstatus: 一个整数,表示测试会话的退出状态码。这可以用来决定脚本的退出状态,例如是否成功执行。
如何使用 pytest_sessionfinish

下面是一个简单的例子来展示如何使用 pytest_sessionfinish 钩子:

import pytest

def pytest_sessionfinish(session, exitstatus):

在这里添加你的逻辑

print("All tests have finished.")
print(f"Exit status: {exitstatus}")

你也可以根据 exitstatus 决定做一些不同的事情

if exitstatus == 0:
print("All tests passed.")
else:
print("Some tests did not pass.")
更复杂的用法

如果你想要创建一个插件,可以像这样定义 pytest_sessionfinish 钩子:

import pytest

class MyPlugin:
def pytest_sessionfinish(self, session, exitstatus):
print("All tests have finished.")
print(f"Exit status: {exitstatus}")

def pytest_configure(config):

注册插件

config.pluginmanager.register(MyPlugin(), "my_plugin")

def pytest_unconfigure(config):

清理插件

plugin = getattr(config, "my_plugin", None)
if plugin is not None:
del config.my_plugin
config.pluginmanager.unregister(plugin)
Session 对象

session 参数是一个 pytest.Session 实例,它提供了许多有用的方法和属性来访问关于测试会话的信息。例如,你可以使用 session.items 获取测试项列表,session.testsfailed 获取失败的测试数量,以及其他有用的统计信息。

Exit Status

exitstatus 是一个整数,表示测试会话的退出状态。常见的值包括:

0: 所有测试通过。
1: 测试失败或命令行选项无效。
2: 用户中断(Ctrl+C)。
5: 内部错误。
你可以根据这个状态码来决定后续的动作,例如发送通知或更新数据库的状态。

通过使用 pytest_sessionfinish 钩子,你可以确保在测试会话结束后执行一些必要的清理工作或通知操作。

pytest+allure测试报告(图文详解)
0.入口python文件执行pytest程序,并生成报告展示
os.system('python -m pytest --alluredir ./allure --clean-alluredir')
time.sleep(3)
os.system('allure serve ./allure')
说明:

    python -m pytest: -m使用pytest模块执行

   --alluredir ./allure:将测试结果保存到 allure目录中.(或者使用 --alluredir=./allure),是pytest-allure-adaptor插件中的选项。

   --clean-alluredir:在保存测试结果之前先清理 allure 目录,是插件pytest-allure-adaptor 中的选项。

————————————————

                        版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/qadnkz/article/details/136333732

标签:


发表评论: