Pytest的基本使用

一: Pytest的安装:

pip install -U pytest -i https://pypi.tuna.tsinghua.edu.cn/simple

二: pytest的简单案例:

  • Pytest能够识别test开头的方法和Test开头的类。
  • 默认不打印,需要指定打印: pytest.main([’-s’])
  • 如果类不以Test开头,类中的方法即使是以test开头也不会执行。
import pytest

def test_01():
    print("我是test_01")

def test_02():
    print("我是test_02")

class Test_demo(object):

    def test_03(self):
        print("我是test_03")

    def test_04(self):
        print("我是test_04")

class Tsts_demo(object):
    def test_05(self):
        print("我是test_05")


if __name__ == '__main__':
    pytest.main(['-s'])

运行结果:
在这里插入图片描述

三: 断言

  • 断言是python自带的,不是unitest中的。
  • 断言的分类: assert断言,异常断言, 警告断言。

1: assert断言:

  • 当断言预测失败时,备注信息会以AssertionError抛出,并在控制台输出。
    案例:
import pytest

def test_1():
    num = 1 + 1
    assert num == 2, 'num==2条件不满足'

def test_2():
    num = 1 + 1
    assert  num == 3, 'num==3条件不满足'

if __name__ == '__main__':
    pytest.main(['-s'])

运行结果:
在这里插入图片描述

2:异常断言

  • 出现指定异常则通过,如果出现不是指定的异常则报错。
  • pytest.raises(预期的异常类,match = 异常的匹配信息)
  • with pytest.raises(TypeError) as 变量: 这个变量存储该异常的所有信息。
  • 变量.__dict__获取变量的所有属性。

案例:
先给定一个闰年的函数作为被测试的函数:

# 判断是否是闰年的函数
def is_leap_year(year):
    # 先判断year是不是整型
    if isinstance(year, int) is not True:
        raise TypeError("传入的参数不是整数")
    elif year == 0:
        raise ValueError("公元元年是从公元一年开始!!")
    elif abs(year) != year:
        raise ValueError("传入的参数不是正整数")
    elif (year % 4 == 0 and year % 100 != 0) or year % 400 == 0:
        print("%d年是闰年" % year)
        return True
    else:
        print("%d年不是闰年" % year)
        return False
测试:抛出的异常和指定的一致
class TestAssert(object):

    def test_01(self):

        with pytest.raises(TypeError):
            # 传入的是字符串,将会抛出TypeError的异常,但是pytest.raises指定的也是TypeError所以测试通过。
            is_leap_year('2020')

在这里插入图片描述

测试: 抛出的异常和测试的异常不一致。
class TestAssert(object):

    def test_01(self):

        with pytest.raises(TypeError):
            # 传入的是0,将会抛出ValueError的异常,但是pytest.raises指定的是TypeError所以测试不通过,抛出异常。
            is_leap_year(0)

在这里插入图片描述

测试:将异常信息存储到变量中。
class TestAssert(object):

    def test_01(self):

        with pytest.raises(TypeError) as err_info:
            # 传入的是0,将会抛出ValueError的异常,但是pytest.raises指定的是TypeError所以测试不通过,抛出异常。
            is_leap_year('2000')
        print("出现的异常类型是:", err_info.type)
        print("异常的所有信息:", err_info.__dict__)

在这里插入图片描述

测试:通过异常的内容捕获异常内容。

匹配异常信息中必须有参数这两个字,并且类型是TypeError。

class TestAssert(object):

    def test_01(self):

        with pytest.raises(TypeError, match="参数") as err_info:
            # 传入的是0,将会抛出ValueError的异常,但是pytest.raises指定的是TypeError所以测试不通过,抛出异常。
            is_leap_year('2000')

在这里插入图片描述

3:警告断言

  • 警告断言最大的区别是变量可以存储多个警告。
  • pytest.warns()

主备工作:自定义三个抛出警告函数:

def make_warn():
    # 抛出版本更新警告
    warnings.warn("deprecated", DeprecationWarning)

def not_warn():
	# 不抛出任何警告
    pass

def user_warn():
	# 抛出兼容性警告
    warnings.warn("user warn", UserWarning)
测试警告断言:
class TestWarns(object):
    # 测试异常和自己定义的一致
    def test_01(self):
        with pytest.warns(DeprecationWarning):
            make_warn()

    # 测试异常和自己的不一致
    def test_02(self):
        with pytest.warns(DeprecationWarning):
            user_warn()

    # 测试没有异常
    def test_03(self):
        with pytest.warns(DeprecationWarning):
            not_warn()

测试结果:
在这里插入图片描述

将警告信息写入变量:
class TestWarns(object):
    # 测试异常和自己定义的一致
    def test_01(self):
        with pytest.warns((DeprecationWarning, UserWarning )) as record:
            make_warn()
            user_warn()

        print("存储异常信息变量的长度:", len(record))
        print("存储异常信息变量第一个的信息:", record[0].message)
        print("存储异常信息变量第一个的异常类型:", record[0].category)
        print("异常信息的所有属性", record.__dict__)

测试结果:
在这里插入图片描述

通过警告内容捕获信息
class TestWarns(object):
    # 测试异常和自己定义的一致
    def test_01(self):
        with pytest.warns((DeprecationWarning), "user") as record:
            make_warn()

结果:因为匹配内容中没有user,所以报错。
在这里插入图片描述

四:setup和teardown函数

  • 函数级别:setup_function() teardown_function()

作用范围:只针对类外面的test函数起作用,每个都执行一次。

  • 类级别: setup_class() teardown_class() setup_method() teardown_method()

setup_class() teardown_class(): 针对当前类只执行一次。
setup_method() teardown_method(): 针对当前类中的方法,每次执行都执行一遍。

  • 模块级别:setup_model() teardown_model()

只有执行当前文件的开始和结束执行一次。

案例:验证函数级别

import pytest

def test_01():
    print("我是普通函数01")
def test_02():
    print("我是普通函数02")

def setup_function():
    print("我是普通函数的前置函数")

def teardown_function():
    print("我是普通函数的后置函数")

在这里插入图片描述

案例:验证类级别

import pytest

class Test_Demo(object):

    def test_01(self):
        print("我是类中函数01")

    def test_02(self):
        print("我是类中函数02")
 	@classmethod
    def setup_class(self):
        print("我是类前置函数")
 	@classmethod
    def teardown_class(self):
        print("我是类后置函数")

    def setup_method(self):
        print("我是类中方法前置函数")

    def teardown_method(self):
        print("我是类中方法后置函数")


if __name__ == '__main__':
    pytest.main()

在这里插入图片描述

案例: 验证模块级别:

import pytest

def setup_module():
    print("我是模块前置函数")

def test_01():
    print("我是测试函数")

def teardown_module():
    print("我是模块后置函数")


if __name__ == '__main__':
    pytest.main()

在这里插入图片描述

五:pytest.fixture的使用

1: fixture的简单使用:

装饰在普通的函数上面,测试函数中传入普通函数的名,测试函数内部可以用普通函数的名,代表普通函数的返回值。

案例: 注意:运行测试函数前先运行被fixture装饰的函数。

import pytest

@pytest.fixture()
def get_url():
    print("我就是个普通函数")
    return "http://www.baidu.com"


def test_web(get_url):
    print("正在运行测试函数")
    print("打印get_url的返回值:", get_url)


if __name__ == '__main__':
    pytest.main()

在这里插入图片描述

2:conftest.py文件的使用

(这个模块里面的被fixture装饰的函数可以直接用)

  • 如果在测试中多个测试文件中用例用到同一个的fixture函数,则可以将其移动到conftest.py文件中, 所需的fixture对象会自动被pytest发现,而不需要再每次导入。

  • conftest.py文件名固定

  • 在conftest.py文件中实现共用的fixture函数

案例:
1: 新建conftest.py:里面定义一个被@pytest.fixture()修饰只返回百度地址的函数。

import pytest

@pytest.fixture()
def get_baidu_url():
    return "www.baidu.com"

2: 另一个文件不导包的情况下直接用conftest中的函数名。

import pytest

def test_01_demo(get_baidu_url):
    print(get_baidu_url)


if __name__ == '__main__':
    pytest.main()

3: 运行结果:
在这里插入图片描述

3:@pytest.mark.usefixtures装饰

作用 : 如果我不想使用被装饰函数的返回值,但是需要函数执行前调用一下被装饰函数,那么可以使用@pytest.mark.usefixtures('被装饰函数名')来处理。
案例:

import pytest

@pytest.fixture()
def return_url():
    print("我是百度地址: www.baidu.com")
    return "www.baidu.com"

@pytest.mark.usefixtures('return_url')
def test_demo():
    print("我是测试函数")

if __name__ == '__main__':
    pytest.main()

在这里插入图片描述

六:fixture参数

1: scope参数:指定被标记函数(方法)的作用域

  • function : 每个测试用例都要执行一次。(默认)
  • class: 作用于整个类,每个类只执行一次。
  • module : 作用域整个模块(文件),每个模块只执行一次。
  • session : 作用域整个项目,每个项目只执行一次。
测试function:
import pytest

@pytest.fixture(scope='function')
def foo():
    print("我是被fixture修饰的函数")
    return "哈喽哈喽......."

def test_01(foo):
    print("我是普通测试01:")
    print(foo)

def test_02(foo):
    print("我是普通测试02")
    print(foo)

class Test_demo(object):
    def test_03(self, foo):
        print("我是类中的测试用例03: ")
        print(foo)

    def test_04(self, foo):
        print("我是类中的测试用例04: ")
        print(foo)

if __name__ == '__main__':
    pytest.main()

在这里插入图片描述

测试:class

修改刚才代码: 变成class,运行结果:注意:非类的测试用例还是每次测试用例都要调用。
在这里插入图片描述

测试: module:

在这里插入图片描述

2: params参数

  • params 参数接收list类型的参数。
  • 对于param里面的每个值,fixture函数都会去遍历执行一次
  • 相应的每次都会驱动使用fixture函数的测试函数执行一次。
    测试
import pytest

@pytest.fixture(params=['renshanwen', '15', '1173714248@qq.com'])
def username(request):
    print("我是被fixture修饰的函数")
    return request.param

def test_demo(username):
    print("我是测试用例")
    print(username)

if __name__ == '__main__':
    pytest.main()

运行结果:

在这里插入图片描述
由于装饰器有列表中有三个,所以测试用例也会执行三次,就会调用三次被装饰函数。

3:autouse参数

  • pytest.fixture(autouse=False) 的autouse参数默认为False, 不会自动执行。
  • 设置为True时,当前运行的所有测试函数在运行前都会执行fixture函数。

演示:

import pytest


@pytest.fixture(autouse=True)
def before():
    print('不管你是谁,都要先调用我')

def test_1():
    print("test_1")

class Test2:
    def test_2(self):
        print('test_2')

    def test_3(self):
        print('test_3')

在这里插入图片描述

七:mark标记

1: pytest.mark.xfail()

1: 将测试函数标记为预期失败。

import pytest

@pytest.mark.xfail(reason = "功能不完善")
def test_1_demo():
    print("我就是个被装饰的")

if __name__ == '__main__':
    pytest.main()

运行结果:
在这里插入图片描述

2:pytest.mark.skip()

1: 无条件跳过测试用例:

import pytest

@pytest.mark.skip(reason = "跳过执行")
def test_1_demo():
    print("我就是个被装饰的")

if __name__ == '__main__':
    pytest.main()

在这里插入图片描述

3:pytest.mark.skipif()

1:有条件地跳过测试函数。

import pytest

@pytest.mark.skipif(condition= True, reason = "跳过")
def test_1_demo():
    print("我就是个被装饰的111111111111111")

@pytest.mark.skipif(condition= False, reason = "不跳过")
def test_2_demo():
    print("我就是个被装饰的222222222222222")


if __name__ == '__main__':
    pytest.main()

2: 运行结果:

在这里插入图片描述

4: pytest.mark.parametrize()

目的:参数化Fixture方法和测试函数

import pytest

@pytest.mark.parametrize(['username', 'password'], [('renshanwen', '12345678'), ('niuniu', '87654321')])
def test_login(username, password):
    print("\n")
    print("username是:", username)
    print("password是:", password)


if __name__ == '__main__':
    pytest.main()

运行结果:
在这里插入图片描述

八:配置文件

  • 配置文件名字, pytest.ini固定。
  • 配置文件中的注释是 ;
  • addopts = -s test_20.py : 指定运行的命令
  • testpaths = ./ : 指定测试文件的路径
  • python_files = test*.py : 识别以test开头的文件
  • python_classes = Test* : 识别以Test开头的类
  • python_functions = mike* : 识别以test开头的函数
    案例:
[pytest]
addopts = -s test_12.py test_13.py 
testpaths = ./
python_files = test_*.py 
python_classes = Test_*
python_functions = test_*
;在ini文件中注释语句是以分号开始的, 所有的注释语句不管多长都是独占一行直到结束的

九:常用插件

1: 插件的安装

workon test_v7 # 进入虚拟环境
pip install pytest-html # 生成测试报告
pip install pytest-ordering # 控制函数执行顺序
pip install pytest-rerunfailures # 失败重试

2:生成测试报告

1: 测试代码:

import pytest

def test_01():
    print("测试成功的测试用例")
    assert True

def test_02():
    print("测试失败的测试用例")
    assert False


if __name__ == '__main__':
    pytest.main()

2: 执行命令:

pytest -s test17_port.py --html=./report.html

3: 报告展示:
在这里插入图片描述

3: 控制测试用例的执行顺序

import pytest

@pytest.mark.run(order= 3)
def test_01():
    print("我是测试用例一")


@pytest.mark.run(order=1)
def test_02():
    print("我是测试用例二")


@pytest.mark.run(order=2)
def test_03():
    print("我是测试用例三")

if __name__ == '__main__':
    pytest.main()

运行结果:
在这里插入图片描述

4: 失败重试

1: 代码:

import pytest

def test_01():
    print("测试成功的测试用例")
    assert True

def test_02():
    print("测试失败的测试用例")
    assert False


if __name__ == '__main__':
    pytest.main()

2:执行命令:

pytest -s test17_port.py --reruns 2

3: 运行结果:
在这里插入图片描述

5:取消插件

  • -p no:ordering : 取消排序
  • -p no:html : 取消生成测试报告
  • -p no:rerunfailures : 取消失败重试

十: Yaml

1:Yaml的语法规则

  • 大小写敏感。

  • 使用缩进表示层级关系。

  • 缩进时不允许使用tab键,只允许使用空格。

  • 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可。

Yaml在线编辑器地址: http://old.qqe2.com/jsontool/yaml.php

2:Yaml数据结构的使用

1: Yaml与python的转换:
注意:Yaml对象冒号后面的空格不能省略。
在这里插入图片描述
2: Yaml中的数组:
在这里插入图片描述
3: Yaml中的锚点和引用:
在这里插入图片描述

3: Python读取Yaml文件和写入Yaml文件

1: 安装PyYaml库:

pip3 install -U PyYAML

2:读取Yaml文件:

准备测试文件test.yaml

Search_Data:
  search_test_001:
    value: 456
    expect: [4,5,6]
  search_test_002:
    value: "你好"
    expect: {"value":"你好"}

yaml.read.py文件中读取test.yaml中的数据:

import yaml

with open("./test.yaml", "r") as f:
  data = yaml.load(f, Loader=yaml.FullLoader)
  print(data

运行结果:
在这里插入图片描述

3:将内容写入到Yaml文件

1: yaml_write.py进行写入

import yaml
data={'Search_Data': {
          'search_test_002': {'expect': {'value': '你好'}, 'value': '你好'}, 
          'search_test_001': {'expect': [4, 5, 6], 'value': 456}
                        }
}
# 要设置编码格式,否则会出现中文乱码
with open('./yaml_hello.yaml', 'w', encoding='utf-8') as f:
    yaml.dump(data, f,allow_unicode=True)

2: 执行结果:
在这里插入图片描述

### Pytest 使用教程 #### 安装 Pytest 为了开始使用 pytest,首先需要安装该库。可以通过 Python 的包管理工具 `pip` 来完成这一操作。 ```bash pip install pytest ``` 一旦安装完毕,就可以创建测试文件来编写测试案例了[^1]。 #### 编写简单的测试函数 pytest 不强制要求特定的方法名前缀或类定义;然而,遵循惯例有助于保持一致性。通常情况下,测试文件以 `test_` 开头,而测试方法也应如此命名以便于识别。 下面展示了一个基本的例子: ```python def add(a, b): return a + b def test_add(): assert add(1, 2) == 3 assert add(-1, 1) == 0 ``` 这段代码中定义了一个名为 `add()` 的简单加法函数以及相应的测试函数 `test_add()`. 测试通过调用被测函数并将实际结果与预期的结果相比较来进行验证. #### 运行测试 保存上述代码到一个叫做 `test_sample.py` 文件里,在命令行输入如下指令即可执行这些测试: ```bash pytest ``` 这将会自动发现所有匹配模式 `test*.py` 或者 `*tests.py` 下面的测试模块,并依次运行其中所有的测试案例. #### 参数化测试 对于具有相似结构但是不同参数集的情况,可以利用 @pytest.mark.parametrize 装饰器简化重复工作量. ```python import pytest @pytest.mark.parametrize( "a,b,sum", [ (1, 2, 3), (-1, 1, 0), (0, 0, 0) ] ) def test_parametrized_addition(a, b, sum): result = add(a, b) assert result == sum ``` 此段脚本展示了如何一次性为多个数据点配置相同的测试逻辑,从而提高了效率并减少了冗余代码的存在. #### 并行执行测试 当项目规模逐渐增大时,串行方式可能会变得低效。这时可借助第三方扩展插件如 pytest-xdist 实现多进程并发处理,进而加快整体进度。 要启用这项特性,先得安装对应的软件包: ```bash pip install pytest-xdist ``` 之后只需加上 `-n N` 参数指定想要启动的工作线程数(这里假设为4),再像平常一样调用 pytest 即可触发分布式计算过程[^2]: ```bash pytest -n 4 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奈何桥上的幽灵野草

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值