pytest框架

⾃动化框架pytest

⽀持Python语⾔的接⼝⾃动化框架有很多,以下是⽀持Python的接⼝⾃动化主流框架对⽐分析:

主流框架对⽐表:

pytest介绍

pytest官⽅⽂档:https://docs.pytest.org/en/stable/getting-started.html

pytest 是⼀个⾮常流⾏且⾼效的Python测试框架,它提供了丰富的功能和灵活的⽤法,使得编写和 运⾏测试⽤例变得简单⽽⾼效。

pytest的优点:

简单易⽤: pytest 的语法简洁清晰,对于编写测试⽤例⾮常友好,⼏乎可以在⼏分钟内上⼿。 • 强⼤的断⾔库: pytest 内置了丰富的断⾔库,可以轻松地进⾏测试结果的判断。

⽀持参数化测试: pytest ⽀持参数化测试,允许使⽤不同的参数多次运⾏同⼀个测试函数,这 ⼤ 提⾼了测试效率。

丰富的插件⽣态系统: pytest 有着丰富的插件⽣态系统,可以通过插件扩展各种功能,⽐如覆 盖率测试b、测试报告⽣成(如 pytest-html 插件可以⽣成完美的HTML测试报告)、失败⽤例重 复执⾏(如 pytest-rerunfailures 插件)等。此外, pytest 还⽀持与selenium、 requests、appinum等结合,实现Web⾃动化、接⼝⾃动化、App⾃动化测试。

灵活的测试控制: pytest 允许跳过指定⽤例,或对某些预期失败的case标记成失败,并⽀持重 复执⾏失败的case。

pytest的安装

安装 pytest 8.3.2 要求 python 版本在3.8及以上。

cpp 复制代码
pip install pytest==8.3.2

可以使用pytest就算安装成功,根据python版本的不同可能安装pytest的版本也需要不同

安装好 pytest 后,确认pycharm中python解释器已经更新,来看⼀下有 pytest 框架和没有 pytest 框架编写代码的区别:

未安装pytest框架的情况下需要编写 main 函数,在 main 函数中⼿动 调⽤测试⽤例test01;安装了 pytest 框架后⽅法名前有直接运⾏标志。

注意:并不是所有的⽅法都可以直接运⾏,需要遵循 pytest 中的⽤例命名规则。

使用pytest的命名规则

  1. ⽂件名必须以 test_ 开头 或者 _test 结尾
  2. 测试类必须以 Test 开头 ,并且不能有 init ⽅法
  3. 测试⽅法必须以 test 开头

当满⾜以上要求后,可通过命令⾏参数 pytest 直接运⾏符合条件的⽤例:

注意:使用pytest框架之后,python类中不可以添加init⽅法

由于 pytest 的测试收集机制,测试类中不可以定义 init ⽅法。 pytest 采⽤⾃动发现机制 来收集测试⽤例。它会⾃动实例化测试类并调⽤其所有以 test 结尾的⽅法作为测试⽤例。如果测试 类中定义了 init ⽅法,那么当 pytest 实例化该类时, init ⽅法会被调⽤,这可能 会掩盖测试类的实际测试逻辑,并引⼊额外的副作⽤,影响测试结果的准确性。

pytest命令参数

pytest 提供了丰富的命令⾏选项来控制测试的执⾏。以下是⼀些常⽤的pytest 命令⾏参数及其 使⽤说明。

pytest配置文件

当前项⽬下创建 pytest.ini ⽂件,该⽂件为 pytest 的配置⽂件,以下为常⻅的配置选项:

pytest.ini文件中

python 复制代码
[pytest]
addopts = -vs
testpaths = ./cases
python_files = test_*.py
python_classes = Test*

配置好 pytest.ini ⽂件后,命令⾏执⾏ pytest 命令即可,⽆需再额外指定其他参数:

pytest.ini ⽂件通常位于项⽬的根⽬录下。通过在pytest.ini 中定义配置项,可以覆盖 pytest 的默认⾏为,以满⾜项⽬的需求。

前后置

使⽤pytest框架,测试类中不可以添加init()⽅法,如何进⾏数据的初始化?

pytest 框架提供三种⽅法做前后置的操作:

  • setup_method 和 teardown_method :这两个⽅法⽤于类中的每个测试⽅法的前置和后置操作。
  • setup_class 和 teardown_class :这两个⽅法⽤于整个测试类的前置和后置操作。
  • fixture :这是 pytest 推荐的⽅式来实现测试⽤例的前置和后置操作。 fixture 提供了更 灵活的控制和更强⼤的功能。

⽰例1: setup_method 和 teardown_method

python 复制代码
class Test():
    def setup_method(self):
        print("setup_method")

    def teardown_method(self):
        print("teardown_method")

    def test_01(self):
        print("test01")

⽰例2: setup_class 和 teardown_class

python 复制代码
class Test():

    def setup_class(self):
        print("setup_class")
        
    def teardown_class(self):
        print("teardown_class")

    def test_01(self):
        print("test01")

断言

断⾔( assert )是⼀种调试辅助⼯具,⽤于检查程序的状态是否符合预期。如果断⾔失败(即条件 为假),Python解释器将抛出⼀个 AssertionError 异常。断⾔通常⽤于检测程序中的逻辑错误。pytest 允许你在Python测试中使⽤标准的Python assert 语句来验证预期和值。

基本语法:

python 复制代码
assert 条件, 错误信息

条件 :必须是⼀个布尔表达式。

错误信息 :当条件为假时显⽰的错误信息,可选。

⽰例1:基本数据类型的断⾔

python 复制代码
#断⾔整数 
a = 1
b = 2
assert a == b
#断⾔字符串 
str = "hello"
assert "hello" == str

⽰例2:数据结构断⾔

python 复制代码
def test():
 # 断⾔列表 
 expect_list = [1, 'apple', 3.14]
 actual_list = [1, 'apple', 3.14]
 # 断⾔元组 
 expect_tuple = (1, 'apple', 3.14)
 actual_tuple = (1, 'apple', 3.14)
 
 # 断⾔字典 
 expect_dict = {'name': 'Alice', 'age': 25}
 actual_dict = {'name': 'Alice', 'age': 25}

 # 断⾔集合 
 expect_set = {1, 2, 3, 'apple'}
 actual_set = {1, 2, 3, 'apple'}
 assert expect_list == actual_list
 assert expect_tuple == actual_tuple
 assert expect_dict == actual_dict
 assert expect_set == actual_set

⽰例3:函数断⾔

python 复制代码
def divide(a, b):
 assert b != 0, "除数不能为0"
 return a / b
# 正常情况 
print(divide(10, 2)) # 输出 5.0 
# 触发断⾔ 
print(divide(10, 0)) # 抛出 AssertionError: 除数不能为0 

⽰例4:接⼝返回值断⾔

python 复制代码
#断⾔接⼝返回值完整字段和值 
def test1():
 url = "http://jsonplaceholder.typicode.com/posts/1"
 r = requests.get(url=url)
 expect_data = {
 "userId": 1,
 "id": 1,
 "title": "sunt aut facere repellat provident occaecati excepturi optio 
reprehenderit",
 "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et 
cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt 
rem eveniet architecto"
 }
 print(r.json())
 assert r.json() == expect_data
 assert r.json()['userId'] == 1

#断⾔接⼝返回值重要字段 
def test2():
 url = "http://jsonplaceholder.typicode.com/comments?postId=1"
 r = requests.get(url=url)
 print(r.json())
 assert r.json()[1]['id'] == 1
 
#断⾔接⼝html返回值 
def test3():
 url = "http://jsonplaceholder.typicode.com/"
 r = requests.get(url=url)
 assert "Use your own data" in r.text

参数化

参数化设计是自动化设计中的一个重要组成部分,它通过定义设计参数和规则,使得设计过程更加灵 活和可控。

pytest中内置的 pytest.mark.parametrize 装饰器允许对测试函数的参数进⾏参数化。

⽰例1:在⽤例上使⽤参数化

python 复制代码
import pytest
@pytest.mark.parametrize("test_input,expected", [("2+4", 6), ("5+3", 8)])
def test_eval(test_input, expected):
    assert eval(test_input) == expected

这⾥, @parametrize 装饰器定义了二个不同的 (test_input,expected) 元组,以便 test_eval 函数将依次使⽤它们运⾏二次。 也可以在类或模块上使⽤ parametrize 标记,这将使⽤参数集调⽤多个函数

⽰例2:在类上使⽤参数化

python 复制代码
import pytest
@pytest.mark.parametrize("n,expected", [(1, 2), (3, 4)])
class TestClass:
 def test_simple_case(self, n, expected):
 assert n + 1 == expected
 def test_weird_simple_case(self, n, expected):
 assert (n * 1) + 1 == expected

要对模块中的所有测试进⾏参数化,你可以将 pytestmark 全局变量赋值:

python 复制代码
pytestmark = pytest.mark.parametrize("n,expected", [(1, 2), (3, 4)])
class TestClass:
 def test_simple_case(self, n, expected):
 assert n + 1 == expected
 def test_weird_simple_case(self, n, expected):
 assert (n * 1) + 1 == expected

除了使⽤ @parametrize 添加参数化外, pytest.fixture() 允许对 fixture 函数进⾏参数化。

⽰例3:⾃定义参数化数据源

python 复制代码
def data_provider():
 return ["a", "b"]

# 定义⼀个测试函数,它依赖于上⾯函数的返回值 
@pytest.mark.parametrize("data", data_provider())
def test_data(data):
 assert data != None
 print(f"Testing with data provider: {data}")

fixture

fixture的介绍

pytest 中的 fixture 是⼀种强⼤的机制,⽤于提供测试函数所需的资源或上下⽂。它可以⽤于 设置测试环境、准备数据等。以下是 fixture 的⼀些核⼼概念和使⽤场景.

基本使用

示例1:使用与不使用fixture标记

未标记fixtrue方法标记:

python 复制代码
#不使用fixtrue标记
def fixtrue_01():
    print("第一个fixtrue_01")

def test_01():
    fixtrue_01()
    print("第一个测试用例")

使用fixtrue标记:

python 复制代码
@pytest.fixture
def fixtrue_01():
    print("第一个fixtrue_01")

def test_01(fixtrue_01):
    print("第一个测试用例")

未标记 fixture ⽅法的调⽤与 fixture 标记的⽅法调⽤完全不⼀样,前者需要在⽅法体中调⽤, ⽽后者可以将函数名作为参数进⾏调⽤

测试脚本中存在的很多重复的代码、公共的数据对象时,使⽤ fixture 最为合适

⽰例2:访问列表⻚和详情⻚之前都需要执⾏登录操作

python 复制代码
@pytest.fixture
def login():
    print("登录")

def test_list(login):
    print("访问列表页")

def test_detail(login):
    print("访问详情页")

运行结果:

通过使用@pytest.fixture 装饰器来告诉pytest⼀个特定函数是⼀个fixture,通过运⾏结果可 ⻅,在执⾏列表⻚和详情⻚之前都会先执⾏ login ⽅法。

fixture嵌套

python 复制代码
@pytest.fixture
def first_entry():
    return 'a'

@pytest.fixture
def order(first_entry):
    return [first_entry]

def test_string(order):
    order.append('b')

    #断言判断
    assert order == ['a', 'b']

测试不必局限于单个 fixture ,它们可以依赖于您想要的任意数量的 fixture ,并且 fixture 也可以使⽤其他 fixture。 pytest 最伟⼤的优势之⼀是其极其灵活的 fixture 系 统,它允许我们将测试的复杂需求简化为更简单和有组织的函数,我们只需要每个函数描述它们所依 赖的事物

请求多个fixture

python 复制代码
class Fruit:
    def __init__(self, fruitname):
        self.fruitname = fruitname

    #类似C++中的重写
    def __eq__(self, otherfruit):
        return self.fruitname == otherfruit.fruitname

@pytest.fixture
def my_fruit():
    return Fruit('apple')

@pytest.fixture
def fruit_basket(my_fruit):
    return [Fruit('banana'), my_fruit]

def test_my_fruit_in_basket(my_fruit, fruit_basket):
    assert my_fruit in fruit_basket

测试和 fixture 不仅限于⼀次请求单个 fixture ,它们可以请求任意多个。

yield fixture

当我们运⾏测试时,我们希望确保它们能够⾃我清理,以便它们不会⼲扰其他测试(同时也避免留下 ⼤量测试数据来膨胀系统)。pytest中的 fixture 提供了⼀个⾮常有⽤拆卸系统,它允许我们为每个 fixture 定义具体的清理步骤。

"Yield" fixture 使⽤ yield ⽽不是 return 。有了这些 fixture ,我们可以运⾏⼀些代 码,并将对象返回给请求的 fixture/test ,就像其他 fixture ⼀样。唯⼀的不同是:

  • return 被替换为 yield 。
  • 该 fixture 的任何拆卸代码放置在 yield 之后。 ⼀旦 pytest 确定了 fixture 的线性顺序,它将运⾏每个 fixture 直到它返回或 yield ,然后 继续执⾏列表中的下⼀个 fixture 做同样的事情。 测试完成后, pytest 将逆向遍历 fixture 列表,对于每个 yield 的 fixture ,运⾏ yield 语句之后的代码。

示例1:

python 复制代码
@pytest.fixture
def open_close():
    print("前置操作,初始化.....")
    yield
    print("后置操作,清理数据.....")

def test_01(open_close):
    print("第⼀个测试⽤例")

⽰例2:创建⽂件句柄与关闭⽂件

python 复制代码
@pytest.fixture
def file_read():
    print("打开文件巨柄")

    fo = open("C:\\code\\python\\apiautotest_1\\cases\\test.txt", "r")
    yield fo
    print("关闭文件")
    fo.close()

@pytest.fixture
def file_write():
    print("关闭打开的句柄")
    fo = open("C:\\code\\python\\apiautotest_1\\cases\\test.txt", "w", encoding="utf-8")
    yield fo

def test_file(file_read, file_write):
    w = file_write
    w.write("写入数据")
    w.close()  #写入之关闭句柄,方便后续读取

    #读取数据
    r = file_read
    str = r.read(10)
    print("文件内容:", str)

带参数的fixture

python 复制代码
pytest.fixture(scope='', params='', autouse='', ids='', name='')

参数详解:

scope 参数⽤于控制fixture的作⽤范围,决定了fixture的⽣命周期。可选值有:

  • function (默认):每个测试函数都会调⽤⼀次fixture。
  • class :在同⼀个测试类中共享这个fixture。
  • module :在同⼀个测试模块中共享这个fixture。(⼀个⽂件⾥)
  • session :整个测试会话中共享这个fixture。

autouse 参数默认为 False 。如果设置为 True ,则每个测试函数都会⾃动调⽤该fixture, ⽆需显式传⼊

params 参数⽤于参数化fixture,⽀持列表传⼊。每个参数值都会使fixture执⾏⼀次,类似于 for循环

ids 参数与 params 配合使⽤,为每个参数化实例指定可读的标识符(给参数取名字)

name 参数⽤于为fixture显式设置⼀个名称。如果使⽤了 name ,则在测试函数中需要使⽤这个 名称来引⽤ fixture (给fixture取名字)

⽰例1: scope 的使⽤

python 复制代码
#scope = "function"
@pytest.fixture(scope="function")
def fixture_01():
    print("初始化")

    yield print("清理")

class TestCase():
    def test_01(self, fixture_01):
        print("test01")

    def test_02(self, fixture_01):
        print("test02")

结果:

python 复制代码
#scope = "function"
@pytest.fixture(scope="class")
def fixture_01():
    print("初始化")

    yield
    print("清理")

class TestCase():
    def test_01(self, fixture_01):
        print("test01")

    def test_02(self, fixture_01):
        print("test02")

结果:

结论:

  • scope 默认为 function ,这⾥的 function 可以省略不写,当 scope="function" 时, 每个测试函数都会调⽤⼀次 fixture 。 scope="class" 时,在同⼀个测试类中, fixture 只会在类中的第⼀个测试函数开始前执⾏⼀次,并在类中的最后⼀个测试函数结束后执⾏清理。
  • 当 scope="moudle" 、 scope="session" 时可⽤于实现全局的前后置应⽤,这⾥需要多个 ⽂件的配合

conftest.py 和 @pytest.fixture 结合使⽤实现全局的前后置应⽤

@pytest.fixtureconftest.py ⽂件结合使⽤,可以实现在多个测试模块( .py )⽂件中共 享前后置操作,这种结合的⽅式使得可以在整个测试项⽬中定义和维护通⽤的前后置逻辑,使测试代码 更加模块化和可维护。

规则:

  • conftest.py 是⼀个单独存放的夹具配置⽂件,名称是固定的不能修改
  • 你可以在项⽬中的不同⽬录下创建多个 conftest.py ⽂件,每个 conftest.py ⽂件都会对其 所在⽬录及其⼦⽬录下的测试模块⽣效
  • 在不同模块的测试中需要⽤到 conftest.py 的前后置功能时,不需要做任何的import导⼊操作
  • 作⽤:可以在不同的 .py ⽂件中使⽤同⼀个 fixture 函数

⽰例2: scope="moudle" 、 scope="session" 实现全局的前后置应⽤

当scope是moudle时

项目结构:

conftest.py:

test_01.py:

test_02.py:

结果:每个测试实例都执行一遍

当scope是session时

conftest.py:

test_01.py:

test_02.py:

结果:每个.py模块执行一次

⽰例3: autouse 的使⽤

python 复制代码
@pytest.fixture(scope="class", autouse=True)
def fixture_02():
    print("初始化")

    yield
    print("清理")

class TestCase1():
    def test_01(self):
        print("test01")

    def test_02(self):
        print("test02")

结果:

autouse 默认为 False ,即当前的 fixture 需要⼿动显⽰调⽤,在该案例之前我们默认使⽤的 都是 autouse=False

当 autouse=True 时, fixture 会在所有测试函数执⾏之前⾃动调⽤,⽆论这些测试函数是否显 式地引⽤了该 fixture

⽰例4:通过 params 实现参数化

python 复制代码
@pytest.fixture(params=["a", "b"])
def data_provider(request):
    return request.param

def test_03(data_provider):
    assert data_provider != None
    print(f"Testing with data provider: {data_provider}")

结果:

相关推荐
兴趣使然黄小黄2 天前
【Pytest】Pytest常用的第三方插件
python·pytest
我的xiaodoujiao2 天前
使用 Python 语言 从 0 到 1 搭建完整 Web UI自动化测试学习系列 34--基础知识 9--文件上传功能
前端·python·测试工具·ui·pytest
我一定会有钱2 天前
pytest测试框架基础
python·单元测试·自动化·pytest
我的xiaodoujiao2 天前
使用 Python 语言 从 0 到 1 搭建完整 Web UI自动化测试学习系列 35--二次封装MySQL数据库连接操作
python·学习·测试工具·pytest
给你一页白纸3 天前
Pytest 测试用例自动生成:接口自动化进阶实践
python·pytest·接口自动化
工会主席-阿冰3 天前
使用pytest-selenium插件,ui自动化示例
selenium·pytest
工会主席-阿冰3 天前
pytest,ui自动化示例
pytest·ui自动化
我一定会有钱5 天前
pytest基础
python·测试工具·测试用例·pytest
西游音月5 天前
(6)pytest+Selenium自动化测试-测试用例编写
selenium·测试用例·pytest