pytest入门教程

快速入门以及怎么使用

在这里不做过多介绍,大家直接去看原文即可。

怎么测试异常

为了测试Exception,可以使用pytest.raises()方法

def f():
    raise ValueError("hello")

def test_exception():
    with pytest.raises(ValueError):
        f()

给case打标记

给测试打标记可用于运行指定的测试案例。

import pytest
import sys

# 直接跳过
@pytest.mark.skip
def test1():
    print(1)

# 预期失败
@pytest.mark.xfail
def test2():
    assert 0

# 有条件的跳过
@pytest.mark.skipif(sys.version_info < (3, 7),
                    reason="requires python3.7 or higher")
def test3():
    pass

=========== 2 skipped, 1 xfailed in 0.06s ========

pytest fixture

fixture是一个很有意思的东西,官方解释如下:

In testing, a fixture provides a defined, reliable and consistent context for the tests. This could include environment (for example a database configured with known parameters) or content (such as a dataset).
Fixtures define the steps and data that constitute the arrange phase of a test (see Anatomy of a test). In pytest, they are functions you define that serve this purpose. They can also be used to define a test’s act phase; this is a powerful technique for designing more complex tests.

先看一下介绍,然后我们演示一些例子:

示例1:多个功能测试都想使用同一批测试数据
import pytest
import numpy as np

@pytest.fixture
def data_1d():
    return np.array([1., 2., 3.])

def test_sqrt(data_1d):
    print("sqrt result:", np.sqrt(data_1d))

def test_max(data_1d):
    print("max result:", np.max(data_1d))

输出如下

sqrt result: [1. 1.41421356 1.73205081]
.max result: 3.0

上面这段代码是怎么能成功运行的呢?这就要讲解fixture的运行原理了,如下是官方解释:

When pytest goes to run a test, it looks at the parameters in that test function’s signature, and then searches for fixtures that have the same names as those parameters. Once pytest finds them, it runs those fixtures, captures what they returned (if anything), and passes those objects into the test function as arguments.

fixture可以被反复使用,每次使用的都是fixture的一个副本,这确保了测试之间不会相互影响。

示例2:同一个测试使用不同的夹具

根据需要,每个测试可以同时使用不同的夹具

import pytest
import numpy as np

@pytest.fixture
def data_1d():
    return np.array([1., 2., 3.])

@pytest.fixture
def data_2d():
    np.random.seed(43)
    return np.random.random((2, 2))

def test_sqrt(data_1d, data_2d):
    print("sqrt result1:", np.sqrt(data_1d))
    print("sqrt result2:", np.sqrt(data_2d))

输出如下:

sqrt result1: [1. 1.41421356 1.73205081]
sqrt result2: [[0.33919694 0.78042715]
[0.36522728 0.49049936]]

示例3:夹具之间的相互使用
import pytest
import numpy as np

@pytest.fixture
def data_1d():
    return np.array([1., 2., 3.])

@pytest.fixture
def data_2d(data_1d):
    return np.concatenate((data_1d, [4, 5, 6]))

def test_sqrt(data_2d):
    print("sqrt result:", np.sqrt(data_2d))

输出结果日下所示:

sqrt result: [1. 1.41421356 1.73205081 2. 2.23606798 2.44948974]

示例4:fixture的自动化使用

有些场景你需要在所有测试case执行之前都执行相同的操作,例如打印日志或者配置环境。我们可以通过设置autouse参数来实现这个目的。

import pytest
import numpy as np


@pytest.fixture(autouse=True)
def use_all():
    print("use use")


def test1():
    pass


def test2():
    pass

输出结果如下所示

use use
.use use

可以看到use_all被调用了两次。

示例5:前处理与后处理

之前演示的案例都是我们在运行某个case之前做的一些操作,但是如果我们想同时做一些后处理呢?例如测试前后都打印一下时间;或者测试之前建立一个环境,测试之后清理环境等。我们可以通过使用yield命令实现这个目的。

import pytest
import numpy as np

@pytest.fixture()
def pre_post():
    # before test
    print("@@@@@@ pre")
    yield None
    # after test
    print("###### post")

def test2(pre_post):
    print("now me")

输出如下所示:

@@@@@@ pre
now me
.###### post

可以看到,通过使用yield,我们成功实现了测试的前处理与后处理需求。

示例6:夹具的参数化使用

测试中常见一种场景是使用夹具来提供测试用例,同一个夹具是否能够提供不同的测试数据(参数化测试)呢?答案是可以的,我们可以通过如下示例来执行参数化测试。

import pytest
import numpy as np


@pytest.fixture(params=[np.ones(5), np.zeros(5)])
def my_data(request): # 接口必需是request
    return request.param # 注意这里是param而不是params


def test2(my_data):
    print("hhhh")
    print(my_data)

输出如下所示:

hhhh
[1. 1. 1. 1. 1.]
.hhhh
[0. 0. 0. 0. 0.]
.

示例7:结合mark使用

有些场景下,某些case我们可能希望它暂时跳过,此时我们也可以通过pytest.param() 来做到这点。

import pytest
import numpy as np


@pytest.fixture(
    params=[np.ones(5),
            np.zeros(5),
            pytest.param(5, marks=pytest.mark.skip)])
def my_data(request):
    return request.param


def test2(my_data):
    print("hhhh")
    print(my_data)

输出如下所示:

hhhh
[1. 1. 1. 1. 1.]
.hhhh
[0. 0. 0. 0. 0.]
.s
============================ 2 passed, 1 skipped in 0.01s =======

更多fixture的用法与详细介绍可参考How to use fixtures

参数化测试

原文链接
参数化测试是我们常见的需求,例如:

  • 对于同一段代码,我们需要不同的数据进行测试
  • 对于很多功能,函数的调用方式与数据都可以是相同的

使用pytest提供的pytest.mark.parametrize装饰器可以轻松的实现以上需求。

示例1:同一段代码,测试不同的输入案例
@pytest.mark.parametrize("data", np.arange(10).reshape((2, 5)))
def test1(data):
    print(data)
    np.srqt(data)

结果为

[0 1 2 3 4]
[5 6 7 8 9]

示例2:测试case交叉组合
@pytest.mark.parametrize("x", np.array([1, 2]))
@pytest.mark.parametrize("y", np.array([3, 4]))
def test2(x, y):
    print(x, y)

.1 3
.2 3
.1 4
.2 4

示例3:每个案例有一组(两个)参数,但不需要两两组合
@pytest.mark.parametrize("x,y", [(10, 20), (30, 40)])
def test3(x, y):
    print(x, y)

.10 20
.30 40

示例4:一个测试模板测试不同函数以及测试数据
@pytest.mark.parametrize("data", np.arange(10).reshape((2, 5)).astype(float))
@pytest.mark.parametrize("func1,func2", [(np.sqrt, np.reciprocal)])
def test4(data, func1, func2):
    print(func1(data))
    print(func2(data))
    assert func1(data) == func2(data)

tmp_path fixture

原文链接
pytest提供了tmp_path功能。可以在测试过程中创建一个tmp_path,用完即毁。
tmp_path是一个pathlib.Path对象

CONTENT = "hello"

def test_create_file(tmp_path):
    print(tmp_path)
    d = tmp_path / "sub"
    d.mkdir()
    p = d / "hello.txt"
    p.write_text(CONTENT)
    assert p.read_text() == CONTENT
    assert len(list(tmp_path.iterdir())) == 1

monkeypatch

原文链接
pytest提供了monkeypatch的fixture,可以实现对其他组件的mock。它有以下方法

monkeypatch.setattr(obj, name, value, raising=True)
monkeypatch.setattr("somemodule.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=None)
monkeypatch.delenv(name, raising=True)
monkeypatch.syspath_prepend(path)
monkeypatch.chdir(path)
示例1:入门示例

我们演示一个示例,用np.max去mock np.sum功能

import pytest
import numpy as np


def mock_sum(data):
    return np.sum(data)


def test1(monkeypatch):
    monkeypatch.setattr(np, "sum", np.max)
    print(mock_sum([1, 2, 3]))

输出结果为

3

全局配置文件conftest.py

这里主要给出两个参考博客:

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[2\]中提到,要使用pytest,首先需要在命令行或Pycharm终端中安装pytest,可以通过运行命令"pip install pytest"来安装。安装完成后,可以通过运行命令"pytest --version"来验证已安装的pytest版本号。接下来,我们可以使用pytest发现测试用例的规则,即将测试用例文件命名为以"test_"开头或以"_test"结尾的py文件,类名以"Test"开头,测试用例以"test_"开头。\[2\] 在执行测试用例时,可以使用pytest命令行来执行,也可以在代码中使用pytest.main()方法来执行测试。\[3\]对于pytest的执行参数,可以根据需要进行配置。例如,可以使用"--pyargs"参数来指定要执行的包,使用"-m"参数来执行被特定装饰器标记的测试用例,使用"-n"参数来指定并发的进程数等。\[3\]\[1\] 综上所述,要使用pytest进行测试,首先需要安装pytest,然后按照pytest的测试用例规则编写测试文件,最后可以通过命令行或代码来执行测试,并根据需要配置执行参数。 #### 引用[.reference_title] - *1* [pytest使用详解](https://blog.csdn.net/chuntingting/article/details/127192385)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v4^insert_chatgpt"}} ] [.reference_item] - *2* [Python测试工具-Pytest使用详解](https://blog.csdn.net/Wenhua_1/article/details/127191189)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v4^insert_chatgpt"}} ] [.reference_item] - *3* [单元测试界的高富帅,Pytest框架,手把手教学,从入门到精通(一)](https://blog.csdn.net/Androidyuexia/article/details/121619390)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v4^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值