【2023最新最火教程】Pytest 自动化测试框架 (超详细~)

1651 篇文章 64 订阅
1548 篇文章 56 订阅
Pytest是一个灵活且功能丰富的第三方测试框架,相比Unittest更易于使用,具有更好的兼容性和扩展性,支持插件系统以适应各种测试场景。文章介绍了Pytest的安装、基本使用,包括测试文件命名规范、断言、测试用例标记、异常处理、参数化测试以及固件(fixtures)的概念,如预处理、后处理、作用域和参数化。固件可用于创建可重用的预处理和后处理逻辑,提升测试效率。
摘要由CSDN通过智能技术生成

Pytest和Unittest测试框架的区别?

如何区分这两者,很简单unittest作为官方的测试框架,在测试方面更加基础,并且可以再次基础上进行二次开发,同时在用法上格式会更加复杂;而pytest框架作为第三方框架,方便的地方就在于使用更加灵活,并且能够对原有unittest风格的测试用例有很好的兼容性,同时在扩展上更加丰富,可通过扩展的插件增加使用的场景,比如一些并发测试等;

Pytest 安装

pip安装:

pip install pytest
复制代码

测试安装成功:

pytest --help

py.test --help
复制代码

检查安装版本:

pytest --version
复制代码

Pytest 示例

Pytest编写规则:

  • 测试文件以test_开头(以_test为结尾)
  • 测试的类以Test开头;
  • 测试的方法以test_开头
  • 断言使用基本的assert

test_example.py

def count_num(a: list) -> int:
    return len(a)


def test_count():
    assert count_num([1, 2, 3]) != 3
复制代码

执行测试:

pytest test_example.py
复制代码

执行结果:

C:\Users\libuliduobuqiuqiu\Desktop\GitProjects\PythonDemo\pytest>pytest test_example.py -v
================================================================= test session starts =================================================================
platform win32 -- Python 3.6.8, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 -- d:\coding\python3.6\python.exe
cachedir: .pytest_cache
rootdir: C:\Users\libuliduobuqiuqiu\Desktop\GitProjects\PythonDemo\pytest
plugins: Faker-8.11.0
collected 1 item                                                                                                                                       

test_example.py::test_count FAILED                                                                                                               [100%]

====================================================================== FAILURES =======================================================================
_____________________________________________________________________ test_count ______________________________________________________________________

    def test_count():
>       assert count_num([1, 2, 3]) != 3
E       assert 3 != 3
E        +  where 3 = count_num([1, 2, 3])

test_example.py:11: AssertionError
=============================================================== short test summary info ===============================================================
FAILED test_example.py::test_count - assert 3 != 3
================================================================== 1 failed in 0.16s ==================================================================
复制代码

备注:

  • .代表测试通过,F代表测试失败;
  • -v显示详细的测试信息, -h显示pytest命令详细的帮助信息;

标记

默认情况下,pytest会在当前目录下寻找以test_为开头(以_test结尾)的测试文件,并且执行文件内所有以test_为开头(以_test为结尾)的所有函数和方法;

  1. 指定运行测试用例,可以通过::显示标记(文件名::类名::方法名)(文件名::函数名)
pytest test_example3.py::test_odd
复制代码
  1. 指定一些测试用例测试运行,可以使用-k模糊匹配
pytest -k example
复制代码
  1. 通过pytest.mark.skip()或者pytest.makr.skipif()条件表达式,跳过指定的测试用例
import pytest

test_flag = False

@pytest.mark.skip()
def test_odd():
    num = random.randint(0, 100)
    assert num % 2 == 1


@pytest.mark.skipif(test_flag is False, reason="test_flag is False")
def test_even():
    num = random.randint(0, 1000)
    assert num % 2 == 0
复制代码
  1. 通过pytest.raises()捕获测试用例可能抛出的异常
def test_zero():
    num = 0
    with pytest.raises(ZeroDivisionError) as e:
        num = 1/0
    exc_msg = e.value.args[0]
    print(exc_msg)
    assert num == 0
复制代码
  1. 预先知道测试用例会失败,但是不想跳过,需要显示提示信息,使用pytest.mark.xfail()
@pytest.mark.xfail()
def test_sum():
    random_list = [random.randint(0, 100)  for x in range(10)]
    num = sum(random_list)
    assert num < 20
复制代码
  1. 对测试用例进行多组数据测试,每组参数都能够独立执行一次(可以避免测试用例内部执行单组数据测试不通过后停止测试)
@pytest.mark.parametrize('num,num2', [(1,2),(3,4)])
def test_many_odd(num: int, num2: int):
    assert num % 2 == 1
    assert num2 % 2 == 0
复制代码

固件(Fixture)

固件就是一些预处理的函数,pytest会在执行测试函数前(或者执行后)加载运行这些固件,常见的应用场景就有数据库的连接和关闭(设备连接和关闭)

简单使用

import pytest


@pytest.fixture()
def postcode():
    return "hello"


def test_count(postcode):
    assert postcode == "hello"
复制代码

按照官方的解释就是当运行测试函数,会首先检测运行函数的参数,搜索与参数同名的fixture,一旦pytest找到,就会运行这些固件,获取这些固件的返回值(如果有),并将这些返回值作为参数传递给测试函数;

预处理和后处理

接下来进一步验证关于官方的说法:

import pytest


@pytest.fixture()
def connect_db():
    print("Connect Database in .......")
    yield
    print("Close Database out .......")


def read_database(key: str):
    p_info = {
        "name": "zhangsan",
        "address": "China Guangzhou",
        "age": 99
    }
    return p_info[key]


def test_count(connect_db):
    assert read_database("name") == "zhangsan"
复制代码

执行测试函数结果:

============================= test session starts =============================
platform win32 -- Python 3.6.8, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 -- D:\Coding\Python3.6\python.exe
cachedir: .pytest_cache
rootdir: C:\Users\libuliduobuqiuqiu\Desktop\GitProjects\PythonDemo\pytest
plugins: Faker-8.11.0
collecting ... collected 1 item

test_example.py::test_count Connect Database in .......
PASSED                                       [100%]Close Database out .......


============================== 1 passed in 0.07s ==============================
复制代码

备注:

  • 首先从结果上看验证了官方的解释,pytest执行测试函数前会寻找同名的固件加载运行;
  • connect_db固件中有yield,这里pytest默认会判断yield关键词之前的代码属于预处理,会在测试前执行,yield之后的代码则是属于后处理,将在测试后执行;

作用域

从前面大致了解了固件的作用,抽离出一些重复的工作方便复用,同时pytest框架中为了更加精细化控制固件,会使用作用域来进行指定固件的使用范围,(比如在这一模块中的测试函数执行一次即可,不需要模块中的函数重复执行)更加具体的例子就是数据库的连接,这一连接的操作可能是耗时的,我只需要在这一模块的测试函数运行一次即可,不需要每次都运行。

而定义固件是,一般通过scop参数来声明作用,常用的有:

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


@pytest.fixture(scope="function")
def func_scope():
    print("func_scope")


@pytest.fixture(scope="module")
def mod_scope():
    print("mod_scope")


@pytest.fixture(scope="session")
def sess_scope():
    print("session_scope")


def test_scope(sess_scope, mod_scope, func_scope):
    pass


def test_scope2(sess_scope, mod_scope, func_scope):
    pass
复制代码

执行结果:

============================= test session starts =============================
platform win32 -- Python 3.6.8, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 -- D:\Coding\Python3.6\python.exe
cachedir: .pytest_cache
rootdir: C:\Users\libuliduobuqiuqiu\Desktop\GitProjects\PythonDemo\pytest
plugins: Faker-8.11.0
collecting ... collected 2 items

test_example2.py::test_scope session_scope
mod_scope
func_scope
PASSED                                      [ 50%]
test_example2.py::test_scope2 func_scope
PASSED                                     [100%]

============================== 2 passed in 0.07s ==============================
复制代码

从这里可以看出module,session作用域的固件只执行了一次,可以验证官方的使用介绍

自动执行

有人可能会说,这样子怎么那么麻烦,unittest框架中直接定义setUp就能自动执行预处理,同样的pytest框架也有类似的自动执行; pytest框架中固件一般通过参数autouse控制自动运行。

import pytest


@pytest.fixture(scope='session', autouse=True)
def connect_db():
   print("Connect Database in .......")
   yield
   print("Close Database out .......")


def test1():
   print("test1")


def test2():
   print("test")
复制代码

执行结果:

============================= test session starts =============================
platform win32 -- Python 3.6.8, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 -- D:\Coding\Python3.6\python.exe
cachedir: .pytest_cache
rootdir: C:\Users\libuliduobuqiuqiu\Desktop\GitProjects\PythonDemo\pytest
plugins: Faker-8.11.0
collecting ... collected 2 items

test_example.py::test1 Connect Database in .......
PASSED                                            [ 50%]test1

test_example.py::test2 PASSED                                            [100%]test
Close Database out .......


============================== 2 passed in 0.07s ==============================
复制代码

从结果看到,测试函数运行前后自动执行了connect_db固件;

参数化

前面简单的提到过了@pytest.mark.parametrize通过参数化测试,而关于固件传入参数时则需要通过pytest框架中内置的固件request,并且通过request.param获取参数

import pytest


@pytest.fixture(params=[
    ('redis', '6379'),
    ('elasticsearch', '9200')
])
def param(request):
    return request.param


@pytest.fixture(autouse=True)
def db(param):
    print('\nSucceed to connect %s:%s' % param)

    yield

    print('\nSucceed to close %s:%s' % param)


def test_api():
    assert 1 == 1
复制代码

执行结果:

============================= test session starts =============================
platform win32 -- Python 3.6.8, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 -- D:\Coding\Python3.6\python.exe
cachedir: .pytest_cache
rootdir: C:\Users\libuliduobuqiuqiu\Desktop\GitProjects\PythonDemo\pytest
plugins: Faker-8.11.0
collecting ... collected 2 items

test_example.py::test_api[param0] 
Succeed to connect redis:6379
PASSED                                 [ 50%]
Succeed to close redis:6379

test_example.py::test_api[param1] 
Succeed to connect elasticsearch:9200
PASSED                                 [100%]
Succeed to close elasticsearch:9200


============================== 2 passed in 0.07s ==============================
复制代码

这里模拟连接redis和elasticsearch,加载固件自动执行连接然后执行测试函数再断开连接。

总结

对于开发来说为什么也要学习自动化测试这一块,很重要的一点就是通过自动化测试节省一些重复工作的时间,同时对于优化代码结构,提高代码覆盖率,以及后续项目重构都是有着很重要的意义,同时理解pytest和unittest在基础上有何区别有助于不同的业务场景中选择适合自己的测试工具。


这篇文章只是简单的介绍了pytest的基本使用,有兴趣的可以去看看官方文档,官方文档中还提到了如内置固件的使用,常用测试的场景等等。如果文章对你有帮助,记得点赞,收藏,加关注。会不定期分享一些干货哦......

软件测试学习资源分享

最后感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

下方这份完整的软件测试视频学习教程已经上传CSDN官方认证的二维码,朋友们如果需要可以自行免费领取 【保证100%免费】

这些资料,对于想进阶【自动化测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!凡事要趁早,特别是技术行业,一定要提升技术功底。希望对大家有所帮助……基础知识、Linux必备、Shell、互联网程序原理、Mysql数据库、抓包工具专题、接口测试工具、测试进阶-Python编程、Web自动化测试、APP自动化测试、接口自动化测试、测试高级持续集成、测试架构开发测试框架、性能测试、安全测试等配套学习资源免费分享

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值