• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Pytest学习笔记

武飞扬头像
拿么娜娜
帮助2

API文档地址:API Reference — pytest documentation

命名规则

  1. 文件名需以test_开头或者_test结尾
  2. 类名需以 Test开头
  3. 方法需以test开头

若不按照命名规则,使用pytest一次性运行多个用例时,不按照命名规则的文件/case将被跳过

运行顺序与运行命令

  1. 默认执行顺序:文件之间按照ASCII顺序,0~9——>A~Z——>a~z,文件内的用例为从上往下执行
  2. 使用某些插件可以改变文件执行顺序:pytest-ordering,或者随机pytest-random-ordering
  3. 退出码:当case运行完成后,控制台打印出运行信息,最终有总结退出码是什么,例如exit code为0

有case失败时,exit code为1,当使用脚本运行完成后,命令行中执行echo $?可以查看对应exit code的执行情况。

Exit code 0

All tests were collected and passed successfully

Exit code 1

Tests were collected and run but some of the tests failed

Exit code 2

Test execution was interrupted by the user(执行时使用Crtl C中断执行)

Exit code 3

Internal error happened while executing tests(脚本中有内部错误)

Exit code 4

pytest command line usage error(执行的命令行参数错误)

Exit code 5

No tests were collected(没有test被执行)

  1. pytest -x:当第一个用例失败后直接停止;pytest --maxfail=2:当两个用例执行失败后再停止
  2. pytest还可以指定目录、模块、类、方法,根据名字、标签运行用例
  • 指定目录:pytest 目录
  • 指定模块:pytest 目录/模块
  • 指定类:pytest 目录/模块::类
  • 指定方法:pytest 目录/模块::类::方法
  • 根据名字:pytest -k 名字
  • 不包含xx:pytest -k "not xxx"
  • xx与xx:pytest -k "xx and xx"
  • xx或xx:pytest -k "xx or xx"
  • 根据标签:在方法上添加装饰器:@pytest.mark.webtes(标签名)。执行:pytest -m webtest
  1. PDB:pytest内置的调试,pytest --pdb命令当test失败时进入PDB调试模式,但需要在控制台输入命令进行调试,建议直接使用断点调试。
  2. 调用python的命令执行Pytest,pytest.main(['param1','param2','path'])
  3. pytest -v:输出更详细的用例执行信息,pytest -s:显示程序中的print和log输出,更多命令详细使用可以执行pytest -h查看帮助

断言与异常

  1. 直接使用assert进行断言,assert 断言表达式,失败message。某些特殊情况的比较:
  • 比较长字符串:显示上下文差异
  • 比较长序列:第一个失败的索引
  • 比较字典:不同的条目
  1. 使用pytest.raises()可以抛出已知异常。放在函数内,with pytest.raises()
  2. 使用装饰器@pytest.mark,xfail(raises=异常)来检查异常。
  3. 使用钩子函数自定义异常:pytest_assertrepr_compare()

fixture

  1. 某方法加上@pytest.fixture装饰器表示该方法是一个夹具函数。当有方法的参数是该夹具函数时,会实例化夹具函数,并返回夹具函数中的返回值。例:
import pytest




class Fruit:
    def __init__(self, name):
        self.name = name
        self.cubed = False


    def cube(self):
        self.cubed = True




class FruitSalad:
    def __init__(self, *fruit_bowl):
        self.fruit = fruit_bowl
        self._cube_fruit()


    def _cube_fruit(self):
        for fruit in self.fruit:
            fruit.cube()




# Arrange
@pytest.fixture
def fruit_bowl():
    return [Fruit("apple"), Fruit("banana")]




def test_fruit_salad(fruit_bowl):
    # Act
    fruit_salad = FruitSalad(*fruit_bowl)


    # Assert
    assert all(fruit.cubed for fruit in fruit_salad.fruit)
学新通

fruit_bowl()就是一个fixture,test_fruit_salad()方法中的参数有fruit_bowl(),他会先实例化fruit_bowl(),获取到fixture返回的值后,再执行test_fruit_salad()下面的语句。上述fixtrue使用替换成手动调用如下:

def fruit_bowl():
    return [Fruit("apple"), Fruit("banana")]




def test_fruit_salad(fruit_bowl):
    # Act
    fruit_salad = FruitSalad(*fruit_bowl)


    # Assert
    assert all(fruit.cubed for fruit in fruit_salad.fruit)




# Arrange
bowl = fruit_bowl()      #实例化
test_fruit_salad(fruit_bowl=bowl)    #将实例化后的方法当参数传进去
学新通
  1. fixture可以请求其他的fixture
  2. fixture可以重复使用,不同的方法可以调用同一个fixture,且相互之间互不干扰,每个方法都可以从夹具中获取到自己的结果
  3. 一个测试方法或者fixture可以一次请求多个fixture,即参数中可以带多个fixture
  4. 自动夹具:在装饰器中加上autouse=True,表示该fixture即使在方法中未请求,他也会自动帮你的所有测试方法请求该fixture
@pytest.fixture(autouse=True)
def append_first(order, first_entry):
    return order.append(first_entry)
  1. fixture的scope:function,class,module,package,session
  • function: the default scope, the fixture is destroyed at the end of the test.
  • class: the fixture is destroyed during teardown of the last test in the class.
  • module: the fixture is destroyed during teardown of the last test in the module.
  • package: the fixture is destroyed during teardown of the last test in the package.
  • session: the fixture is destroyed at the end of the test session.

MonkeyPatching

  1. 猴子补丁:属性在运行时被动态替换,只在代码运行时发挥作用,即在内存中,不会修改修改源码,因此只会在当前运行的程序实例中有效。
  2. 猴子补丁破坏了封装,而且容易导致程序与补丁代码的实现细节紧密耦合,所以被视为临时的变通方案,不是集成代码的推荐方式。
  3. 主要方法如下:
monkeypatch.setattr(obj, name, value, raising=True)  //设置属性
monkeypatch.delattr(obj, name, raising=True)     //删除属性
monkeypatch.setitem(mapping, name, value)        //设置字典
monkeypatch.delitem(obj, name, raising=True)     //删除字典
monkeypatch.setenv(name, value, prepend=False)    //设置环境变量
monkeypatch.delenv(name, raising=True)           //删除环境变量
monkeypatch.syspath_prepend(path)            //修改sys.path 
monkeypatch.chdir(path)           //更改当前工作路径

参数化

pytest有三种参数化方式:

1.@pytest.fixture,使用fixture的返回值来传参

2.使用@pytest.mark.parametrize 允许在测试函数或类中定义多组参数,在用例中实现参数化

3.使用钩子函数:pytest_generate_tests 允许定义自定义参数化方案或扩展:详细说明文档:pytest文档69-Hook函数之参数化生成测试用例pytest_generate_tests - 上海-悠悠 - 博客园 (cnblogs.com)

@pytest.mark.parametrize("test_input,expected", [("3 5", 8), ("2 4", 6), ("6*9", 42)])

1.表示传入test_input和expected两个参数,值用元组表示,一个元组代表一组值。有三组值,表示该方法会执行三次,每次用不同的值。

2.在@pytest.mark.parametrize中定义的参数,在方法中要传入该形参。

3.@pytest.mark.parametrize中可声明scope作用域,默认为function,表示加载完所有参数后在一个方法上执行。scope还可以取值:class,module,表示每一个参数在执行完当前类或者模块后再替换为下一个参数。

@pytest.mark.parametrize("x", [0, 1])
@pytest.mark.parametrize("y", [2, 3])

两个作用在同一个方法上,表示使用笛卡尔积的方式取参,即x=0/y=2, x=1/y=2, x=0/y=3, and x=1/y=3 这几组值。

SKIP

  • pytest可以跳过某些测试方法
  1. 无条件跳过:@pytest.mark.skip(reason=""),不需要满足任何条件,直接跳过该case,且在控制台使用-rs打印出reason
  2. 在特定条件下跳过:@pytest.mark.skipif(条件,reason=""),满足条件时,跳过该case,且在控制台使用-rs打印出reason(使用后发现条件中无法引用变量,引用了就会报找不到该变量)
  3. 当导入的模块缺失的时候跳过:@pytest.importskip("aaa"),当没有import aaa模块时,跳过该case
  • pytest还可以跳过class、module和文件、目录
  • 当case需要复用某些makers时,可以将maker写在文件中,用一个变量表示,然后直接@该变量即可,跨文件使用时则先导入该变量,再@引用

XFail

pytest可以使一些测试方法在预期中失败

  1. 无条件失败:@pytest.mark.xfail(reason),装饰在测试方法上,该方法仍会执行但是不会报错,且在控制台会标记该方法为XFail,打印出reason(reason可选)
  2. 在特定条件下失败:@pytest.mark.xfail(条件,reason=""),满足条件时,该方法会标记为xfail,且在控制台打印出reason
  3. 抛出指定异常失败:@pytest.mark.xfail(raises=RuntimeError),可以指定单个异常,或者多个异常,多个异常使用元组表示。该方法失败的原因是RuntimeError,如果该方法失败但是raises未声明该异常,则会被当成常规的失败处理
  4. 将方法标记为xfail,该方法仍会执行;若想把该方法标记为xfail,但不想执行,使用run参数,设置为false即可:@pytest.mark.xfail(run=False)
  5. 使用参数strict=True,可以将标记为xfail的方法结果变为xpass("unexpectedly passing":出乎意料的通过)
  6. pytest -runxfail,使用该命令运行,所有方法都会被打上xfail标记,即使该方法没有加xfail标记,但这样@pytest.mark.xfail就会不起作用

Skip/xfail with parametrize

skip和xfail也可以集成在参数中:

pytest.param()也可用于传参

import pytest


@pytest.mark.parametrize(
    ("n", "expected"),
    [
        (1, 2),
        pytest.param(1, 0, marks=pytest.mark.xfail),
        pytest.param(1, 3, marks=pytest.mark.xfail(reason="some bug")),
        (2, 3),
        (3, 4),
        (4, 5),
        pytest.param(
            10, 11, marks=pytest.mark.skipif(sys.version_info >= (3, 0), reason="py2k")
        ),
    ],
)
def test_increment(n, expected):
    assert n   1 == expected
学新通

pytest-dependency

  • 用例依赖插件,使用前需要install pytest-dependency
  • 使用场景:当test2依赖于test1时,就可以使用该插件,即当test1失败了,test2就会跳过不执行,当test1执行成功了,test2才会执行
  • 使用方法:在被依赖的方法上进行标记:@pytest.mark.dependency(name=''),name一般为test的名称(注:一定要添加name属性,不然依赖方法很容易找不到被依赖的方法),在依赖的方法上进行标记:@pytest.mark.dependency(depends=['被依赖的name']),依赖方法中也可以同时添加name,被其他的方法依赖
  • 若执行case时不想真的fail,可以在被依赖方法中使用pytest.xfail()来标记失败

doctest-文档测试

doctest是Python中自带的一个模块玩,为单元测试的一种,doctest 模块会搜索那些看起来像交互式会话的 Python 代码片段,然后尝试执行并验证结果。在pytest中也可以运行doctest,pytest中文件格式为test*.txt的文件将会被默认为doctest运行

  1. 格式:在需要运行的代码前面加上>>>,期望的结果前不加
# content of test_example.txt


hello this is a doctest         //不会执行
>>> x = 3                       //执行的代码
>>> x                           //执行的代码
3                               //期望的结果

2.运行命令:①可直接pytest运行.txt文件 ②pytest --doctest-modules 文件名运行指定文件

Unittest

unittest是Python自带的另一种测试框架,在unittest.TestCase中可以使用pytest的某些属性:

  • Marks:skip,skipif,xfail
  • Auto-use fixtures 即pytest.fixture(autouse=true)

还有三种属性在unittest.TestCase可能可以运行成功,可能无法运行成功:

  • Fixtures(除去autouse的fixtures)
  • Parametrization
  • custom hooks

Nose

nose也是Python的一种测试框架,使用前需要install。和pytest一样都适用于单元测试,但是分别使用Pytest和nose发现:pytest遇到错误会打印出详细的错误信息,而nose不会,只是抛出错误名称

在nose框架中也可以使用Pytest的某些属性,详细看官方文档。

石墨文档链接:石墨文档

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhhkbhae
系列文章
更多 icon
同类精品
更多 icon
继续加载