快乐学习
前程无忧、中华英才非你莫属!

Pytest全栈自动化测试指南-入门

Pytest介绍

Pytest是一个成熟的全功能Python测试工具,可以帮助您编写更好的程序。该pytest框架使编写可读测试变得容易,并且可以扩展以支持应用程序和库的复杂功能测试。pytest需要:Python 3.7+ 或 PyPy3。

Pytest特点

开箱即用.自动发现测试用例简化断言语句,统一为assert丰富的插件架构,超过 800 多个外部插件具有灵活的扩展性和方便的参数化方法

安装&使用

$ pip install  pytest
$ pytest --version
pytest 7.1.2

命名规则&断言

  1. 模块:test_.py 或_test.py。
  2. 函数:test*。
  3. 类:Test*,测试类不能有init函数。
  4. 断言:assert ,支持所有python的布尔表达式

用例执行顺序

  1. 在包含用例的项目根路径下(root-dir)执行:pytest -v
  2. 目录和py文件:按照ascii码排序方式,进行文件递归顺序收集和运行
  3. py文件中:按照代码从上到下
  4. 验证执行顺序:pytest --collect-only

命令行选项

pytest自动化测试

assert 布尔表达式

  • [x] 比较运算符:【>】【<】【<=】【>=】【==】【!=】
    例如:assert actual == expected

  • [x] 身份和成员运算符: 【is】、【is not】、【in】、【not in】
    例如: assert expected in actual

  • [x] 逻辑运算符和布尔函数:【not】【and】【or】【startswith()】
    例如 :assert not expected
    例如 :assert version.startswith("3.8")

待测student功能

# ---------------------------------student.py--------------------------------------
# 以字典的形式返回学生的姓名和成绩
def student_score():
    return {'小明': 99, '小红': 100}

# 以列表的形式返回学生的姓名
def student_name():
    return ['小明', '小红']

编写pass 与fail 用例

# ---------------------------------test_01_pass_fail.py---------------------------
# 这是通过用例
def test_pass():
    # 期望值
    expected = 1
    # 实际值
    actual = 1
    # 通过(实际值)跟(预期值)进行相等比较,相等通过,不相等报错
    assert actual == expected

# 这是失败,不通过用例
def test_fail():
    # 期望值
    expected = 2
    # 实际值
    actual = 3
    # 通过(实际值)跟(预期值)进行相等比较,相等通过,不相等报错
    assert actual == expected

# ---------------------------------test_02_datatype.py----------------------------
from btest.Day1.student import student_score, student_name

# 这是student_score的验证全数据等式用例
def test_student_score():
    # 期望值
    expected = {'小红': 100, '小明': 99}
    # 实际值
    actual = student_score()
    # 通过(实际值)跟(预期值)进行相等比较,相等通过,不相等报错
    assert actual == expected

# 这是student_score 的验证部分数据等式用例
# 验证小红是否等于100,是:pass 、否:fail
def test_student_score_other():
    # 期望值
    expected = 100
    # 实际值
    actual = student_score()['小红']
    # 通过(实际值)跟(预期值)进行相等比较,相等通过,不相等报错
    assert actual == expected

# 这是student_name 的包含用例:
# 验证小明是否在学生姓名列表中,是:pass(通过), 否:fail(失败)
def test_student_name():
    # 期望值
    expected = '小明123123'
    # 实际值
    actual = student_name()
    # 通过判断(预期数据)是否在实际数据中,在:pass,不在:fail
    assert len(actual) == 2
    assert expected in actual

多断言

# ---------------------------------test_03_many_assert.py-------------------------
# 安装: pip install pytest-assume -i https://pypi.douban.com/simple
from pytest_assume.plugin import assume

from btest.Day1.student import student_score

def test_student_score_hard():
    actual = student_score()

    with assume: # 实际数据类型是否跟预期数据类型一致。
        assert isinstance(actual, dict)

    with assume: # 实际的数据长度是否跟预期长度相同
        assert len(actual) == 3

    with assume: # 预期数据是否在实际数据内
        assert '小红123123' in actual

    with assume: # 实际数据是否等于预期数据
        assert actual['小红'] == 60

自定义异常信息

# ---------------------------------test_04_custom_fail.py-------------------------
import pytest
from pytest_assume.plugin import assume

from btest.Day1.student import student_name

def test_student_name_hard_custom_fail():
    actual = student_name()
    with assume:  # 实际的数据长度是否跟预期长度相同
        assert len(actual) == 3, \
            f"实际数据长度:{len(actual)} 与预期数据长度:{3}不相同!!"

    with assume:  # 预期数据是否在实际数据内
        assert '小红' in actual, \
            f"预期数据:{'小红'}没在实际数据中:{actual}!!"

    if '小乐' not in actual:
        pytest.fail(f"预期数据:小乐,没有在实际数据{actual}中")

处理异常

# ---------------------------------test_05_exceptions.py--------------------------
import pytest

from btest.Day1.student import student_score

# 通用try/finally 处理用例异常
def test_student_score_error():
    actual = student_score()
    try:
        actual['小红'] = actual['小红'] + '10'
    except:
        pass
    finally:
        assert '小乐' in actual, "小乐没有在学生成绩表里!!"

# pytest用例异常处理
def test_student_score_raise():
    actual = student_score()
    with pytest.raises(Exception):  # 更具可读性且不易出错。
        actual['小红'] = actual['小红'] + '10'
        # assert '小乐' in actual # 不会执行
    # 会执行
    assert '小乐' in actual, \
        f" 预期数据:小乐,没有在实际数据中{actual}!!"

# ---------------------------------test_06_get_error.py---------------------------
"""
1、获取用例的所在的模块、
2、获取用例的名称、
3、获取用例的具体错误信息。
"""

import inspect  # 从实时 Python 对象中获取有用的信息

import pytest

# python-inspect 捕获详细异常信息
def test_inspect_info():
    # 期望值为 模块名::用例名::错误信息
    expected = 'test_06_get_error::test_inspect_info::division by zero'

    with pytest.raises(Exception) as exc_info:
        c = 2 / 0

    # 获取错误文本:exc_info.value.args[0]
    # 如获取错误类型:exc_info.type
    error_info = exc_info.value.args[0]  # 获取用例具体错误信息

    module_name = inspect.getmodulename(__file__)  # 获取用例所在模块
    method_name = inspect.stack()[0][3]  # [0][3]获取用例名字,[1][4][0]获取用例名字和参数

    actual = f"{module_name}::{method_name}::{error_info}"
    assert actual == expected

# Pytest内置功能捕获异常信息
def test_get_raises(request):
    # 期望值为 模块名::用例名::错误信息
    expected = 'test_06_get_error::test_get_raises::division by zero'

    with pytest.raises(Exception) as exc_info:
        c = 2 / 0

    error_info = exc_info.value.args[0]

    module_name = request.module.__name__.split('.')[-1]  # 获取模块 包名.test_06xxx
    method_name = request.function.__name__  # 获取用例名字
    actual = f"{module_name}::{method_name}::{error_info}"
    assert actual == expected

数据类

Dataclass这个模块提供了一个类装饰器和一些函数,用于自动添加生成的 special method,例如 init() 和 repr() 到用户定义的类。 它最初描述于 PEP 557 .

对标准库的补充,称为数据类。可以将数据类视为“具有默认值的可变命名元组”

数据类使用普通的类定义语法,你可以自由地使用继承、元类、文档字符串、用户定义的方法、类工厂和其他 Python 类特性.

装饰器: https://www.ztloo.com/2021/11/06/decorators/

Python术语对照社区文档: https://docs.python.org/zh-cn/3/glossary.html?highlight=decorator

special method(特殊方法) :https://docs.python.org/zh-cn/3/glossary.html#term-special-method

PEP 557(Python 增强建议) :https://www.python.org/dev/peps/pep-0557

标准库:https://docs.python.org/zh-cn/3/library/index.html

命名元组: https://docs.python.org/zh-cn/3/library/collections.html?highlight=namedtuple

# ---------------------------------face_to_object.py------------------------------
from dataclasses import asdict, dataclass, astuple, replace

# https://docs.python.org/3/library/dataclasses.html?highlight=dataclass#module-dataclasses

@dataclass
class GirlFriend:
    # 节省了__init__函数
    name: str
    age: int
    height: int = 170  # cm
    weight: int = 50  # kg

    @classmethod
    def from_dict(cls, d):
        return GirlFriend(**d)

    def to_dict(self):
        return asdict(self)

    def to_tuple(self):
        return astuple(self)

    def update(self, **changes):
        return replace(self, **changes)

if __name__ == '__main__':
    # 把字典转行成类对象
    ym_dict = {'name': '杨幂', 'age': 18, 'height': 170, 'weight': 68}
    ym = GirlFriend.from_dict(ym_dict)
    print('我是类对象型杨幂', ym)

    # 把类对象转化成字典
    ym_dict = ym.to_dict()
    print('我是字典型杨幂:', ym_dict)

    # 把类对象转化成元组
    ym_tuple = ym.to_tuple()
    print(f'我是元组型杨幂:{ym_tuple}')

    # 把杨幂修改成刘亦菲
    lyf = ym.update(name='刘亦菲', age=22)
    print(f'我是刘亦菲:{lyf}')

自定义辅助断言函数

import pytest

from btest.Day2.face_to_object import GirlFriend

def assert_identical(gf_1, gf_2):
    if gf_1.name != gf_2.name:
        pytest.fail(f"name不匹配. {gf_1.name} != {gf_2.name}")

def test_identical():
    gf_1 = GirlFriend("刘亦菲", 18, 170, 60)
    gf_2 = GirlFriend("刘亦菲", 18, 170, 60)
    assert_identical(gf_1, gf_2)

更多案例,更多内容,请点击: http://weike.fm/KzXbA994f

打赏
赞(1) 打赏
未经允许不得转载:同乐学堂 » Pytest全栈自动化测试指南-入门

特别的技术,给特别的你!

联系QQ:1071235258QQ群:710045715

觉得文章有用就打赏一下文章作者

非常感谢你的打赏,我们将继续给力更多优质内容,让我们一起创建更加美好的网络世界!

支付宝扫一扫打赏

微信扫一扫打赏

error: Sorry,暂时内容不可复制!